winverbs: fix race in async connect handling
[mirror/winof/.git] / core / complib / cl_threadpool.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
4  *\r
5  * This software is available to you under the OpenIB.org BSD license\r
6  * below:\r
7  *\r
8  *     Redistribution and use in source and binary forms, with or\r
9  *     without modification, are permitted provided that the following\r
10  *     conditions are met:\r
11  *\r
12  *      - Redistributions of source code must retain the above\r
13  *        copyright notice, this list of conditions and the following\r
14  *        disclaimer.\r
15  *\r
16  *      - Redistributions in binary form must reproduce the above\r
17  *        copyright notice, this list of conditions and the following\r
18  *        disclaimer in the documentation and/or other materials\r
19  *        provided with the distribution.\r
20  *\r
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
25  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
26  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
28  * SOFTWARE.\r
29  *\r
30  * $Id$\r
31  */\r
32 \r
33 \r
34 /*\r
35  * Abstract:\r
36  *      Implementation of thread pool.\r
37  *\r
38  * Environment:\r
39  *      All\r
40  */\r
41 \r
42 \r
43 #include <complib/cl_threadpool.h>\r
44 #include <complib/cl_atomic.h>\r
45 #include <complib/cl_memory.h>\r
46 \r
47 void\r
48 __cl_thread_pool_routine(\r
49         IN      void* const     context )\r
50 {\r
51         cl_status_t                     status = CL_SUCCESS;\r
52         cl_thread_pool_t        *p_thread_pool = (cl_thread_pool_t*)context;\r
53 \r
54         /* Continue looping until signalled to end. */\r
55         while( !p_thread_pool->exit )\r
56         {\r
57                 /* Wait for the specified event to occur. */\r
58                 status = cl_event_wait_on( &p_thread_pool->wakeup_event, \r
59                                                         EVENT_NO_TIMEOUT, TRUE );\r
60 \r
61                 /* See if we've been signalled to end execution. */\r
62                 if( (p_thread_pool->exit) || (status == CL_NOT_DONE) )\r
63                         break;\r
64 \r
65                 /* The event has been signalled.  Invoke the callback. */\r
66                 (*p_thread_pool->pfn_callback)( (void*)p_thread_pool->context );\r
67         }\r
68 \r
69         /*\r
70          * Decrement the running count to notify the destroying thread\r
71          * that the event was received and processed.\r
72          */\r
73         cl_atomic_dec( &p_thread_pool->running_count );\r
74         cl_event_signal( &p_thread_pool->destroy_event );\r
75 }\r
76 \r
77 \r
78 void\r
79 cl_thread_pool_construct(\r
80         IN      cl_thread_pool_t* const p_thread_pool )\r
81 {\r
82         CL_ASSERT( p_thread_pool);\r
83 \r
84         cl_memclr( p_thread_pool, sizeof(cl_thread_pool_t) );\r
85         cl_event_construct( &p_thread_pool->wakeup_event );\r
86         cl_event_construct( &p_thread_pool->destroy_event );\r
87         cl_list_construct( &p_thread_pool->thread_list );\r
88         p_thread_pool->state = CL_UNINITIALIZED;\r
89 }\r
90 \r
91 \r
92 cl_status_t\r
93 cl_thread_pool_init(\r
94         IN      cl_thread_pool_t* const         p_thread_pool,\r
95         IN      uint32_t                                        count,\r
96         IN      cl_pfn_thread_callback_t        pfn_callback,\r
97         IN      const void* const                       context,\r
98         IN      const char* const                       name )\r
99 {\r
100         cl_status_t     status;\r
101         cl_thread_t     *p_thread;\r
102         uint32_t        i;\r
103 \r
104         CL_ASSERT( p_thread_pool );\r
105         CL_ASSERT( pfn_callback );\r
106 \r
107         cl_thread_pool_construct( p_thread_pool );\r
108 \r
109         if( !count )\r
110                 count = cl_proc_count();\r
111 \r
112         status = cl_list_init( &p_thread_pool->thread_list, count );\r
113         if( status != CL_SUCCESS )\r
114         {\r
115                 cl_thread_pool_destroy( p_thread_pool );\r
116                 return( status );\r
117         }\r
118 \r
119         /* Initialize the event that the threads wait on. */\r
120         status = cl_event_init( &p_thread_pool->wakeup_event, FALSE );\r
121         if( status != CL_SUCCESS )\r
122         {\r
123                 cl_thread_pool_destroy( p_thread_pool );\r
124                 return( status );\r
125         }\r
126 \r
127         /* Initialize the event used to destroy the threadpool. */\r
128         status = cl_event_init( &p_thread_pool->destroy_event, FALSE );\r
129         if( status != CL_SUCCESS )\r
130         {\r
131                 cl_thread_pool_destroy( p_thread_pool );\r
132                 return( status );\r
133         }\r
134 \r
135         p_thread_pool->pfn_callback = pfn_callback;\r
136         p_thread_pool->context = context;\r
137 \r
138         for( i = 0; i < count; i++ )\r
139         {\r
140                 /* Create a new thread. */\r
141                 p_thread = (cl_thread_t*)cl_malloc( sizeof(cl_thread_t) );\r
142                 if( !p_thread )\r
143                 {\r
144                         cl_thread_pool_destroy( p_thread_pool );\r
145                         return( CL_INSUFFICIENT_MEMORY );\r
146                 }\r
147 \r
148                 cl_thread_construct( p_thread );\r
149 \r
150                 /*\r
151                  * Add it to the list.  This is guaranteed to work since we\r
152                  * initialized the list to hold at least the number of threads we want\r
153                  * to store there.\r
154                  */\r
155                 status = cl_list_insert_head( &p_thread_pool->thread_list, p_thread );\r
156                 CL_ASSERT( status == CL_SUCCESS );\r
157 \r
158                 /* Start the thread. */\r
159                 status = cl_thread_init( p_thread, __cl_thread_pool_routine,\r
160                         p_thread_pool, name );\r
161                 if( status != CL_SUCCESS )\r
162                 {\r
163                         cl_thread_pool_destroy( p_thread_pool );\r
164                         return( status );\r
165                 }\r
166 \r
167                 /*\r
168                  * Increment the running count to insure that a destroying thread\r
169                  * will signal all the threads.\r
170                  */\r
171                 cl_atomic_inc( &p_thread_pool->running_count );\r
172         }\r
173         p_thread_pool->state = CL_INITIALIZED;\r
174         return( CL_SUCCESS );\r
175 }\r
176 \r
177 \r
178 void\r
179 cl_thread_pool_destroy(\r
180         IN      cl_thread_pool_t* const p_thread_pool )\r
181 {\r
182         cl_thread_t             *p_thread;\r
183 \r
184         CL_ASSERT( p_thread_pool );\r
185         CL_ASSERT( cl_is_state_valid( p_thread_pool->state ) );\r
186 \r
187         /* Indicate to all threads that they need to exit. */\r
188         p_thread_pool->exit = TRUE;\r
189 \r
190         /*\r
191          * Signal the threads until they have all exited.  Signalling\r
192          * once for each thread is not guaranteed to work since two events\r
193          * could release only a single thread, depending on the rate at which\r
194          * the events are set and how the thread scheduler processes notifications.\r
195          */\r
196         while( p_thread_pool->running_count )\r
197         {\r
198                 cl_event_signal( &p_thread_pool->wakeup_event );\r
199                 /*\r
200                  * Wait for the destroy event to occur, indicating that the thread\r
201                  * has exited.\r
202                  */\r
203                 cl_event_wait_on( &p_thread_pool->destroy_event,\r
204                         2000000, TRUE );\r
205         }\r
206 \r
207         /*\r
208          * Stop each thread one at a time.  Note that this cannot be done in the\r
209          * above for loop because signal will wake up an unknown thread.\r
210          */\r
211         if( cl_is_list_inited( &p_thread_pool->thread_list ) )\r
212         {\r
213                 while( !cl_is_list_empty( &p_thread_pool->thread_list ) )\r
214                 {\r
215                         p_thread =\r
216                                 (cl_thread_t*)cl_list_remove_head( &p_thread_pool->thread_list );\r
217                         cl_thread_destroy( p_thread );\r
218                         cl_free( p_thread );\r
219                 }\r
220         }\r
221 \r
222         cl_event_destroy( &p_thread_pool->destroy_event );\r
223         cl_event_destroy( &p_thread_pool->wakeup_event );\r
224         cl_list_destroy( &p_thread_pool->thread_list );\r
225         p_thread_pool->state = CL_UNINITIALIZED;\r
226 }\r
227 \r
228 \r
229 cl_status_t\r
230 cl_thread_pool_signal(\r
231         IN      cl_thread_pool_t* const p_thread_pool )\r
232 {\r
233         CL_ASSERT( p_thread_pool );\r
234         CL_ASSERT( p_thread_pool->state == CL_INITIALIZED );\r
235 \r
236         return( cl_event_signal( &p_thread_pool->wakeup_event ) );\r
237 }\r