10ea097e822d2ad6d47652cae85a431889978300
[pthrlib.git] / pthr_wait_queue.c
1 /* Wait queues.
2  * by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: pthr_wait_queue.c,v 1.5 2002/12/01 14:29:30 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33
34 #ifdef HAVE_ASSERT_H
35 #include <assert.h>
36 #endif
37
38 #include <pool.h>
39 #include <vector.h>
40
41 #include "pthr_pseudothread.h"
42 #include "pthr_wait_queue.h"
43
44 /* See implementation notes in <pthr_wait_queue.h>. */
45 struct wait_queue
46 {
47   /* List of threads currently sleeping on the queue. */
48   vector sleepers;
49 };
50
51 wait_queue
52 new_wait_queue (pool pool)
53 {
54   wait_queue wq = pmalloc (pool, sizeof *wq);
55
56   wq->sleepers = new_vector (pool, pseudothread);
57   return wq;
58 }
59
60 int
61 wq_nr_sleepers (wait_queue wq)
62 {
63   return vector_size (wq->sleepers);
64 }
65
66 /* To sleep on the wait queue, we register ourselves, then we swap back
67  * into the reactor context.
68  */
69 void
70 wq_sleep_on (wait_queue wq)
71 {
72   vector_push_back (wq->sleepers, current_pth);
73
74   /* Swap context back to the calling context. */
75   _pth_switch_thread_to_calling_context ();
76
77   /* When we get here, we have been woken up ... */
78
79   /* Have we been signalled? */
80   if (_pth_alarm_received ())
81     {
82       int i;
83       pseudothread p;
84
85       /* Remove self from sleepers list. */
86       for (i = 0; i < vector_size (wq->sleepers); ++i)
87         {
88           vector_get (wq->sleepers, i, p);
89           if (p == current_pth)
90             {
91               vector_erase (wq->sleepers, i);
92               goto found;
93             }
94         }
95
96       /* Oops - not found on sleepers list. */
97       abort ();
98
99     found:
100       /* Exit. */
101       pth_exit ();
102     }
103 }
104
105 /* This is the prepoll handler which actually wakes up the threads. */
106 struct wake_up_info
107 {
108   pool pool;
109   vector sleepers;              /* Pseudothreads to wake up. */
110   reactor_prepoll handler;      /* Handler (must be unregistered at end). */
111 };
112
113 static void
114 do_wake_up (void *infop)
115 {
116   struct wake_up_info *info = (struct wake_up_info *) infop;
117   int i;
118
119   for (i = 0; i < vector_size (info->sleepers); ++i)
120     {
121       pseudothread pth;
122
123       vector_get (info->sleepers, i, pth);
124
125       /* Swap into the thread context. */
126       _pth_switch_calling_to_thread_context (pth);
127     }
128
129   reactor_unregister_prepoll (info->handler);
130   delete_pool (info->pool);
131 }
132
133 /* To wake up we take a private copy of the wait queue, clear the
134  * sleepers list, then register a prepoll handler which will eventually
135  * run and wake up each sleeper in turn.
136  */
137 static inline void
138 wake_up (wait_queue wq, int n)
139 {
140   pool pool;
141   vector v;
142   reactor_prepoll handler;
143   struct wake_up_info *wake_up_info;
144
145   /* Added this experimentally to get around a bug when rws running monolith
146    * apps which have database connections open is killed. It seems to be
147    * something to do with having prepoll handlers registered when the
148    * reactor exits. Avoid this entirely here - there is no need, as far as
149    * I can see, to do anything in this function if no one is actually sleeping
150    * on the queue.
151    * - RWMJ 2002/10/15
152    */
153   if (vector_size (wq->sleepers) == 0) return;
154
155   /* This will be freed up by the prepoll handler. */
156   pool = new_subpool (global_pool);
157
158   /* Take a private copy, either of the whole queue, or just part of it,
159    * and also clear the list.
160    */
161   if (n == -1)
162     {
163       v = copy_vector (pool, wq->sleepers);
164       vector_clear (wq->sleepers);
165     }
166   else
167     {
168       v = new_vector (pool, pseudothread);
169
170       while (n > 0)
171         {
172           pseudothread pth;
173
174           vector_pop_front (wq->sleepers, pth);
175           vector_push_back (v, pth);
176           n--;
177         }
178     }
179
180   /* Register a prepoll handler to wake up these sleepin' bewts. */
181   wake_up_info = pmalloc (pool, sizeof *wake_up_info);
182   wake_up_info->pool = pool;
183   wake_up_info->sleepers = v;
184   handler = reactor_register_prepoll (pool, do_wake_up, wake_up_info);
185   wake_up_info->handler = handler;
186 }
187
188 void
189 wq_wake_up (wait_queue wq)
190 {
191   wake_up (wq, -1);
192 }
193
194 void
195 wq_wake_up_one (wait_queue wq)
196 {
197   /* If there is nothing on the wait queue, but we were instructed to
198    * wake one, then there is probably a bug in the code.
199    */
200   if (vector_size (wq->sleepers) < 1) abort ();
201
202   wake_up (wq, 1);
203 }