1 /* Support for chains recording users of a resource; `struct hurd_userlink'.
2    Copyright (C) 1994-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_USERLINK_H
20 
21 #define	_HURD_USERLINK_H	1
22 #include <features.h>
23 
24 #define __need_NULL
25 #include <stddef.h>
26 
27 #if defined __USE_EXTERN_INLINES && defined _LIBC
28 # if IS_IN (libc)
29 #  include <hurd/signal.h>
30 # endif
31 #endif
32 #include <setjmp.h>
33 
34 
35 /* This structure records a link in two doubly-linked lists.
36    We call these the per-resource user list and the per-thread
37    active-resource list.
38 
39    Users of a given resource are recorded by their presence in a list
40    associated with that resource.  A user attaches his own link (in local
41    storage on his stack) to a shared chain at the time he begins using some
42    resource.  When finished with that resource, the user removes his link
43    from the chain.  If his link is the last (there are no other users of
44    the resource), and his chain has been detached from the shared cell (the
45    resource in the cell has been replaced), then the user deallocates the
46    resource that he used.
47 
48    All uses of shared resources by a single thread are linked together by
49    its `active-resource' list; the head of this list is stored in the
50    per-thread sigstate structure.  When the thread makes a non-local exit
51    (i.e. longjmp), it will examine its active-resource list, and each link
52    residing in a stack frame being jumped out of will be unlinked from both
53    the resource's user list and the thread's active-resource list, and
54    deallocate the resource if that was the last user link for that resource.
55 
56    NOTE: Access to a thread's active-resource list must always be done
57    inside a signal-proof critical section; the functions in this file
58    assume they are called inside a critical section, and do no locking of
59    their own.  Also important: the longjmp cleanup relies on all userlink
60    structures residing on the stack of the using thread.  */
61 
62 struct hurd_userlink
63   {
64     struct
65       {
66 	struct hurd_userlink *next, **prevp;
67       } resource, thread;
68 
69     /* This function is called when a non-local exit
70        unwinds the frame containing this link.  */
71     void (*cleanup) (void *cleanup_data, jmp_buf env, int val);
72     void *cleanup_data;
73   };
74 
75 
76 #ifndef _HURD_USERLINK_H_EXTERN_INLINE
77 #define _HURD_USERLINK_H_EXTERN_INLINE __extern_inline
78 #endif
79 
80 
81 /* Attach LINK to the chain of users at *CHAINP.  */
82 
83 extern void
84 _hurd_userlink_link (struct hurd_userlink **chainp,
85 		     struct hurd_userlink *link);
86 
87 #if defined __USE_EXTERN_INLINES && defined _LIBC
88 # if IS_IN (libc)
89 _HURD_USERLINK_H_EXTERN_INLINE void
_hurd_userlink_link(struct hurd_userlink ** chainp,struct hurd_userlink * link)90 _hurd_userlink_link (struct hurd_userlink **chainp,
91 		     struct hurd_userlink *link)
92 {
93   struct hurd_userlink **thread_chainp;
94 
95   link->resource.next = *chainp;
96   if (link->resource.next)
97     link->resource.next->resource.prevp = &link->resource.next;
98   link->resource.prevp = chainp;
99   *chainp = link;
100 
101   if (!__LIBC_NO_TLS ())
102     {
103       /* Also chain it on the current thread's list of active resources.  */
104       thread_chainp = &_hurd_self_sigstate ()->active_resources;
105       link->thread.next = *thread_chainp;
106       if (link->thread.next)
107 	link->thread.next->thread.prevp = &link->thread.next;
108       link->thread.prevp = thread_chainp;
109       *thread_chainp = link;
110     }
111 }
112 # endif
113 #endif
114 
115 
116 /* Detach LINK from its chain.  Returns nonzero iff this was the
117    last user of the resource and it should be deallocated.  */
118 
119 extern int _hurd_userlink_unlink (struct hurd_userlink *link);
120 
121 #if defined __USE_EXTERN_INLINES && defined _LIBC
122 # if IS_IN (libc)
123 _HURD_USERLINK_H_EXTERN_INLINE int
_hurd_userlink_unlink(struct hurd_userlink * link)124 _hurd_userlink_unlink (struct hurd_userlink *link)
125 {
126   /* We should deallocate the resource used if this chain has been detached
127      from the cell (and thus has a nil `prevp'), and there is no next link
128      representing another user reference to the same resource. */
129   int dealloc = ! link->resource.next && ! link->resource.prevp;
130 
131   /* Remove our link from the chain of current users.  */
132   if (link->resource.prevp)
133     *link->resource.prevp = link->resource.next;
134   if (link->resource.next)
135     link->resource.next->resource.prevp = link->resource.prevp;
136 
137   if (!__LIBC_NO_TLS ())
138     {
139       /* Remove our link from the chain of currently active resources
140 	 for this thread.  */
141       *link->thread.prevp = link->thread.next;
142       if (link->thread.next)
143 	link->thread.next->thread.prevp = link->thread.prevp;
144     }
145 
146   return dealloc;
147 }
148 # endif
149 #endif
150 
151 /* Relocate LINK to NEW_LINK.
152    To be used when e.g. reallocating a link array.  */
153 
154 extern void _hurd_userlink_move (struct hurd_userlink *new_link,
155                                 struct hurd_userlink *link);
156 
157 #if defined __USE_EXTERN_INLINES && defined _LIBC
158 # if IS_IN (libc)
159 _HURD_USERLINK_H_EXTERN_INLINE void
_hurd_userlink_move(struct hurd_userlink * new_link,struct hurd_userlink * link)160 _hurd_userlink_move (struct hurd_userlink *new_link,
161                      struct hurd_userlink *link)
162 {
163   *new_link = *link;
164 
165   if (new_link->resource.next != NULL)
166     new_link->resource.next->resource.prevp = &new_link->resource.next;
167   *new_link->resource.prevp = new_link;
168 
169   if (!__LIBC_NO_TLS ())
170     {
171       if (new_link->thread.next != NULL)
172 	new_link->thread.next->thread.prevp = &new_link->thread.next;
173       *new_link->thread.prevp = new_link;
174     }
175 }
176 # endif
177 #endif
178 
179 /* Clear all users from *CHAINP.  Call this when the resource *CHAINP
180    protects is changing.  If the return value is nonzero, no users are on
181    the chain and the caller should deallocate the resource.  If the return
182    value is zero, someone is still using the resource and they will
183    deallocate it when they are finished.  */
184 
185 extern int _hurd_userlink_clear (struct hurd_userlink **chainp);
186 
187 #if defined __USE_EXTERN_INLINES && defined _LIBC
188 # if IS_IN (libc)
189 _HURD_USERLINK_H_EXTERN_INLINE int
_hurd_userlink_clear(struct hurd_userlink ** chainp)190 _hurd_userlink_clear (struct hurd_userlink **chainp)
191 {
192   if (*chainp == NULL)
193     return 1;
194 
195   /* Detach the chain of current users from the cell.  The last user to
196      remove his link from that chain will deallocate the old resource.  */
197   (*chainp)->resource.prevp = NULL;
198   *chainp = NULL;
199   return 0;
200 }
201 # endif
202 #endif
203 
204 #endif	/* hurd/userlink.h */
205