1 | /* Lightweight user references for ports. |
2 | Copyright (C) 1993-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #ifndef _HURD_PORT_H |
20 | |
21 | #define _HURD_PORT_H 1 |
22 | #include <features.h> |
23 | |
24 | #include <mach.h> |
25 | #include <hurd/userlink.h> |
26 | #include <spin-lock.h> |
27 | |
28 | |
29 | /* Structure describing a cell containing a port. With the lock held, a |
30 | user extracts PORT, and attaches his own link (in local storage) to the |
31 | USERS chain. PORT can then safely be used. When PORT is no longer |
32 | needed, with the lock held, the user removes his link from the chain. |
33 | If his link is the last, and PORT has changed since he fetched it, the |
34 | user deallocates the port he used. See <hurd/userlink.h>. */ |
35 | |
36 | struct hurd_port |
37 | { |
38 | spin_lock_t lock; /* Locks rest. */ |
39 | struct hurd_userlink *users; /* Chain of users; see below. */ |
40 | mach_port_t port; /* Port. */ |
41 | }; |
42 | |
43 | |
44 | /* Evaluate EXPR with the variable `port' bound to the port in PORTCELL. */ |
45 | /* Also see HURD_PORT_USE_CANCEL. */ |
46 | |
47 | #define HURD_PORT_USE(portcell, expr) \ |
48 | ({ struct hurd_port *const __p = (portcell); \ |
49 | struct hurd_userlink __link; \ |
50 | const mach_port_t port = _hurd_port_get (__p, &__link); \ |
51 | __typeof(expr) __result = (expr); \ |
52 | _hurd_port_free (__p, &__link, port); \ |
53 | __result; }) |
54 | |
55 | |
56 | #ifndef _HURD_PORT_H_EXTERN_INLINE |
57 | #define _HURD_PORT_H_EXTERN_INLINE __extern_inline |
58 | #endif |
59 | |
60 | |
61 | /* Initialize *PORT to INIT. */ |
62 | |
63 | extern void _hurd_port_init (struct hurd_port *port, mach_port_t init); |
64 | |
65 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
66 | # if IS_IN (libc) |
67 | _HURD_PORT_H_EXTERN_INLINE void |
68 | _hurd_port_init (struct hurd_port *port, mach_port_t init) |
69 | { |
70 | __spin_lock_init (&port->lock); |
71 | port->users = NULL; |
72 | port->port = init; |
73 | } |
74 | # endif |
75 | #endif |
76 | |
77 | |
78 | /* Cleanup function for non-local exits. */ |
79 | extern void _hurd_port_cleanup (void *, jmp_buf, int); |
80 | |
81 | /* Get a reference to *PORT, which is locked. |
82 | Pass return value and LINK to _hurd_port_free when done. */ |
83 | |
84 | extern mach_port_t |
85 | _hurd_port_locked_get (struct hurd_port *port, |
86 | struct hurd_userlink *link); |
87 | |
88 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
89 | # if IS_IN (libc) |
90 | _HURD_PORT_H_EXTERN_INLINE mach_port_t |
91 | _hurd_port_locked_get (struct hurd_port *port, |
92 | struct hurd_userlink *link) |
93 | { |
94 | mach_port_t result; |
95 | result = port->port; |
96 | if (result != MACH_PORT_NULL) |
97 | { |
98 | link->cleanup = &_hurd_port_cleanup; |
99 | link->cleanup_data = (void *) result; |
100 | _hurd_userlink_link (&port->users, link); |
101 | } |
102 | __spin_unlock (&port->lock); |
103 | return result; |
104 | } |
105 | # endif |
106 | #endif |
107 | |
108 | /* Same, but locks PORT first. */ |
109 | |
110 | extern mach_port_t |
111 | _hurd_port_get (struct hurd_port *port, |
112 | struct hurd_userlink *link); |
113 | |
114 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
115 | # if IS_IN (libc) |
116 | _HURD_PORT_H_EXTERN_INLINE mach_port_t |
117 | _hurd_port_get (struct hurd_port *port, |
118 | struct hurd_userlink *link) |
119 | { |
120 | mach_port_t result; |
121 | HURD_CRITICAL_BEGIN; |
122 | __spin_lock (&port->lock); |
123 | result = _hurd_port_locked_get (port, link); |
124 | HURD_CRITICAL_END; |
125 | return result; |
126 | } |
127 | # endif |
128 | #endif |
129 | |
130 | |
131 | /* Relocate LINK to NEW_LINK. |
132 | To be used when e.g. reallocating a link array. */ |
133 | |
134 | extern void |
135 | _hurd_port_move (struct hurd_port *port, |
136 | struct hurd_userlink *new_link, |
137 | struct hurd_userlink *link); |
138 | |
139 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
140 | # if IS_IN (libc) |
141 | _HURD_PORT_H_EXTERN_INLINE void |
142 | _hurd_port_move (struct hurd_port *port, |
143 | struct hurd_userlink *new_link, |
144 | struct hurd_userlink *link) |
145 | { |
146 | HURD_CRITICAL_BEGIN; |
147 | __spin_lock (&port->lock); |
148 | _hurd_userlink_move (new_link, link); |
149 | __spin_unlock (&port->lock); |
150 | HURD_CRITICAL_END; |
151 | } |
152 | # endif |
153 | #endif |
154 | |
155 | |
156 | /* Free a reference gotten with `USED_PORT = _hurd_port_get (PORT, LINK);' */ |
157 | |
158 | extern void |
159 | _hurd_port_free (struct hurd_port *port, |
160 | struct hurd_userlink *link, |
161 | mach_port_t used_port); |
162 | |
163 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
164 | # if IS_IN (libc) |
165 | _HURD_PORT_H_EXTERN_INLINE void |
166 | _hurd_port_free (struct hurd_port *port, |
167 | struct hurd_userlink *link, |
168 | mach_port_t used_port) |
169 | { |
170 | int dealloc; |
171 | if (used_port == MACH_PORT_NULL) |
172 | /* When we fetch an empty port cell with _hurd_port_get, |
173 | it does not link us on the users chain, since there is |
174 | no shared resource. */ |
175 | return; |
176 | HURD_CRITICAL_BEGIN; |
177 | __spin_lock (&port->lock); |
178 | dealloc = _hurd_userlink_unlink (link); |
179 | __spin_unlock (&port->lock); |
180 | HURD_CRITICAL_END; |
181 | if (dealloc) |
182 | __mach_port_deallocate (__mach_task_self (), used_port); |
183 | } |
184 | # endif |
185 | #endif |
186 | |
187 | |
188 | /* Set *PORT's port to NEWPORT. NEWPORT's reference is consumed by PORT->port. |
189 | PORT->lock is locked. */ |
190 | |
191 | extern void _hurd_port_locked_set (struct hurd_port *port, mach_port_t newport); |
192 | |
193 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
194 | # if IS_IN (libc) |
195 | _HURD_PORT_H_EXTERN_INLINE void |
196 | _hurd_port_locked_set (struct hurd_port *port, mach_port_t newport) |
197 | { |
198 | mach_port_t old; |
199 | old = _hurd_userlink_clear (&port->users) ? port->port : MACH_PORT_NULL; |
200 | port->port = newport; |
201 | __spin_unlock (&port->lock); |
202 | if (old != MACH_PORT_NULL) |
203 | __mach_port_deallocate (__mach_task_self (), old); |
204 | } |
205 | # endif |
206 | #endif |
207 | |
208 | /* Same, but locks PORT first. */ |
209 | |
210 | extern void _hurd_port_set (struct hurd_port *port, mach_port_t newport); |
211 | |
212 | #if defined __USE_EXTERN_INLINES && defined _LIBC |
213 | # if IS_IN (libc) |
214 | _HURD_PORT_H_EXTERN_INLINE void |
215 | _hurd_port_set (struct hurd_port *port, mach_port_t newport) |
216 | { |
217 | HURD_CRITICAL_BEGIN; |
218 | __spin_lock (&port->lock); |
219 | _hurd_port_locked_set (port, newport); |
220 | HURD_CRITICAL_END; |
221 | } |
222 | # endif |
223 | #endif |
224 | |
225 | |
226 | #endif /* hurd/port.h */ |
227 | |