libibverbs/comp_mgr: fix hang during destruction
[mirror/winof/.git] / etc / user / comp_channel.cpp
1 /*\r
2  * Copyright (c) 2008, 2009 Intel Corporation.  All rights reserved.\r
3  *\r
4  * This software is available to you under the OpenIB.org BSD license\r
5  * below:\r
6  *\r
7  *     Redistribution and use in source and binary forms, with or\r
8  *     without modification, are permitted provided that the following\r
9  *     conditions are met:\r
10  *\r
11  *      - Redistributions of source code must retain the above\r
12  *        copyright notice, this list of conditions and the following\r
13  *        disclaimer.\r
14  *\r
15  *      - Redistributions in binary form must reproduce the above\r
16  *        copyright notice, this list of conditions and the following\r
17  *        disclaimer in the documentation and/or other materials\r
18  *        provided with the distribution.\r
19  *\r
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV\r
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
27  * SOFTWARE.\r
28  */\r
29 \r
30 #include <comp_channel.h>\r
31 #include <process.h>\r
32 \r
33 static void CompManagerQueue(COMP_MANAGER *pMgr, COMP_ENTRY *pEntry);\r
34 static void CompChannelQueue(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry);\r
35 \r
36 \r
37 /*\r
38  * Completion manager\r
39  */\r
40 \r
41 static unsigned __stdcall CompThreadPoll(void *Context)\r
42 {\r
43         COMP_MANAGER *mgr = (COMP_MANAGER *) Context;\r
44         COMP_ENTRY *entry;\r
45         OVERLAPPED *overlap;\r
46         DWORD bytes;\r
47         ULONG_PTR key;\r
48 \r
49         while (mgr->Run) {\r
50                 GetQueuedCompletionStatus(mgr->CompQueue, &bytes, &key,\r
51                                                                   &overlap, INFINITE);\r
52                 entry = CONTAINING_RECORD(overlap, COMP_ENTRY, Overlap);\r
53 \r
54                 if (entry->Channel) {\r
55                         CompChannelQueue(entry->Channel, entry);\r
56                 } else {\r
57                         CompManagerQueue(mgr, entry);\r
58                 }\r
59         }\r
60 \r
61         _endthreadex(0);\r
62         return 0;\r
63 }\r
64 \r
65 DWORD CompManagerOpen(COMP_MANAGER *pMgr)\r
66 {\r
67         DWORD ret;\r
68 \r
69         InitializeCriticalSection(&pMgr->Lock);\r
70         pMgr->Busy = 0;\r
71         DListInit(&pMgr->DoneList);\r
72         CompEntryInit(NULL, &pMgr->Entry);\r
73 \r
74         pMgr->CompQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, -1);\r
75         if (pMgr->CompQueue == NULL) {\r
76                 ret = GetLastError();\r
77                 goto err1;\r
78         }\r
79 \r
80         pMgr->Event = CreateEvent(NULL, TRUE, TRUE, NULL);\r
81         if (pMgr->Event == NULL) {\r
82                 ret = GetLastError();\r
83                 goto err2;\r
84         }\r
85 \r
86         pMgr->Run = TRUE;\r
87         pMgr->Thread = (HANDLE) _beginthreadex(NULL, 0, CompThreadPoll, pMgr, 0, NULL);\r
88         if (pMgr->Thread == NULL) {\r
89                 ret = GetLastError();\r
90                 goto err3;\r
91         }\r
92         return 0;\r
93 \r
94 err3:\r
95         CloseHandle(pMgr->Event);\r
96 err2:\r
97         CloseHandle(pMgr->CompQueue);\r
98 err1:\r
99         DeleteCriticalSection(&pMgr->Lock);     \r
100         return ret;\r
101 }\r
102 \r
103 void CompManagerClose(COMP_MANAGER *pMgr)\r
104 {\r
105         COMP_CHANNEL *channel;\r
106         COMP_ENTRY entry;\r
107 \r
108         pMgr->Run = FALSE;\r
109         CompEntryInit(NULL, &entry);\r
110         PostQueuedCompletionStatus(pMgr->CompQueue, 0, (ULONG_PTR) pMgr, &entry.Overlap);\r
111         WaitForSingleObject(pMgr->Thread, INFINITE);\r
112         CloseHandle(pMgr->Thread);\r
113 \r
114         CloseHandle(pMgr->CompQueue);\r
115         CloseHandle(pMgr->Event);\r
116         DeleteCriticalSection(&pMgr->Lock);     \r
117 }\r
118 \r
119 DWORD CompManagerMonitor(COMP_MANAGER *pMgr, HANDLE hFile, ULONG_PTR Key)\r
120 {\r
121         HANDLE cq;\r
122 \r
123         cq = CreateIoCompletionPort(hFile, pMgr->CompQueue, Key, 0);\r
124         return (cq == NULL) ? GetLastError() : 0;\r
125 }\r
126 \r
127 static void CompManagerQueue(COMP_MANAGER *pMgr, COMP_ENTRY *pEntry)\r
128 {\r
129         EnterCriticalSection(&pMgr->Lock);\r
130         DListInsertTail(&pEntry->MgrEntry, &pMgr->DoneList);\r
131         SetEvent(pMgr->Event);\r
132         LeaveCriticalSection(&pMgr->Lock);\r
133 }\r
134 \r
135 static void CompManagerRemoveEntry(COMP_MANAGER *pMgr, COMP_ENTRY *pEntry)\r
136 {\r
137         EnterCriticalSection(&pMgr->Lock);\r
138         DListRemove(&pEntry->MgrEntry);\r
139         LeaveCriticalSection(&pMgr->Lock);\r
140 }\r
141 \r
142 DWORD CompManagerPoll(COMP_MANAGER *pMgr, DWORD Milliseconds,\r
143                                           COMP_CHANNEL **ppChannel)\r
144 {\r
145         COMP_ENTRY *entry;\r
146         DWORD ret = 0;\r
147 \r
148         EnterCriticalSection(&pMgr->Lock);\r
149         while (DListEmpty(&pMgr->DoneList)) {\r
150                 ResetEvent(pMgr->Event);\r
151                 LeaveCriticalSection(&pMgr->Lock);\r
152         \r
153                 ret = WaitForSingleObject(pMgr->Event, Milliseconds);\r
154                 if (ret) {\r
155                         return ret;\r
156                 }\r
157 \r
158                 EnterCriticalSection(&pMgr->Lock);\r
159         }\r
160 \r
161         entry = CONTAINING_RECORD(pMgr->DoneList.Next, COMP_ENTRY, MgrEntry);\r
162         *ppChannel = entry->Channel;\r
163         if (entry->Channel == NULL) {\r
164                 DListRemove(&entry->MgrEntry);\r
165                 InterlockedExchange(&entry->Busy, 0);\r
166                 ret = ERROR_CANCELLED;\r
167         }\r
168         LeaveCriticalSection(&pMgr->Lock);\r
169 \r
170         return ret;\r
171 }\r
172 \r
173 void CompManagerCancel(COMP_MANAGER *pMgr)\r
174 {\r
175         if (InterlockedCompareExchange(&pMgr->Entry.Busy, 1, 0) == 0) {\r
176                 PostQueuedCompletionStatus(pMgr->CompQueue, 0, (ULONG_PTR) pMgr,\r
177                                                                    &pMgr->Entry.Overlap);\r
178         }\r
179 }\r
180 \r
181 \r
182 /*\r
183  * Completion channel\r
184  */\r
185 \r
186 DWORD CompChannelInit(COMP_MANAGER *pMgr, COMP_CHANNEL *pChannel, DWORD Milliseconds)\r
187 {\r
188         pChannel->Manager = pMgr;\r
189         pChannel->Head = NULL;\r
190         pChannel->TailPtr = &pChannel->Head;\r
191         pChannel->Milliseconds = Milliseconds;\r
192 \r
193         pChannel->Event = CreateEvent(NULL, TRUE, TRUE, NULL);\r
194         if (pChannel->Event == NULL) {\r
195                 return GetLastError();\r
196         }\r
197 \r
198         InitializeCriticalSection(&pChannel->Lock);\r
199         CompEntryInit(pChannel, &pChannel->Entry);\r
200         return 0;\r
201 }\r
202 \r
203 void CompChannelCleanup(COMP_CHANNEL *pChannel)\r
204 {\r
205         CloseHandle(pChannel->Event);\r
206         DeleteCriticalSection(&pChannel->Lock); \r
207 }\r
208 \r
209 static void CompChannelInsertTail(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
210 {\r
211         *pChannel->TailPtr = pEntry;\r
212         pChannel->TailPtr = &pEntry->Next;\r
213 }\r
214 \r
215 static COMP_ENTRY *CompChannelRemoveHead(COMP_CHANNEL *pChannel)\r
216 {\r
217         COMP_ENTRY *entry;\r
218 \r
219         entry = pChannel->Head;\r
220         pChannel->Head = entry->Next;\r
221         if (pChannel->Head == NULL) {\r
222                 pChannel->TailPtr = &pChannel->Head;\r
223         }\r
224         return entry;\r
225 }\r
226 \r
227 static COMP_ENTRY *CompChannelFindRemove(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
228 {\r
229         COMP_ENTRY **entry_ptr, *entry;\r
230 \r
231         EnterCriticalSection(&pChannel->Lock);\r
232         entry_ptr = &pChannel->Head;\r
233         while (*entry_ptr && *entry_ptr != pEntry) {\r
234                 entry_ptr = &(*entry_ptr)->Next;\r
235         }\r
236 \r
237         entry = *entry_ptr;\r
238         if (entry != NULL) {\r
239                 *entry_ptr = pEntry->Next;\r
240                 if (pChannel->TailPtr == &pEntry->Next) {\r
241                         pChannel->TailPtr = entry_ptr;\r
242                 }\r
243                 CompManagerRemoveEntry(pChannel->Manager, pEntry);\r
244                 InterlockedExchange(&pEntry->Busy, 0);\r
245         }\r
246         LeaveCriticalSection(&pChannel->Lock);\r
247         return entry;\r
248 }\r
249 \r
250 static void CompChannelQueue(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
251 {\r
252         pEntry->Next = NULL;\r
253         EnterCriticalSection(&pChannel->Lock);\r
254         CompManagerQueue(pChannel->Manager, pEntry);\r
255         CompChannelInsertTail(pChannel, pEntry);\r
256         SetEvent(pChannel->Event);\r
257         LeaveCriticalSection(&pChannel->Lock);\r
258 }\r
259 \r
260 DWORD CompChannelPoll(COMP_CHANNEL *pChannel, COMP_ENTRY **ppEntry)\r
261 {\r
262         COMP_ENTRY *entry;\r
263         DWORD ret;\r
264 \r
265         EnterCriticalSection(&pChannel->Lock);\r
266         while (pChannel->Head == NULL) {\r
267                 ResetEvent(pChannel->Event);\r
268                 LeaveCriticalSection(&pChannel->Lock);\r
269 \r
270                 ret = WaitForSingleObject(pChannel->Event, pChannel->Milliseconds);\r
271                 if (ret) {\r
272                         return ret;\r
273                 }\r
274 \r
275                 EnterCriticalSection(&pChannel->Lock);\r
276         }\r
277         entry = CompChannelRemoveHead(pChannel);\r
278         CompManagerRemoveEntry(pChannel->Manager, entry);\r
279         LeaveCriticalSection(&pChannel->Lock);\r
280 \r
281         InterlockedExchange(&entry->Busy, 0);\r
282         *ppEntry = entry;\r
283         ret = (entry == &pChannel->Entry) ? ERROR_CANCELLED : 0;\r
284 \r
285         return ret;\r
286 }\r
287 \r
288 void CompChannelCancel(COMP_CHANNEL *pChannel)\r
289 {\r
290         if (InterlockedCompareExchange(&pChannel->Entry.Busy, 1, 0) == 0) {\r
291                 PostQueuedCompletionStatus(pChannel->Manager->CompQueue, 0,\r
292                                                                    (ULONG_PTR) pChannel, &pChannel->Entry.Overlap);\r
293         }\r
294 }\r
295 \r
296 \r
297 /*\r
298  * Completion entry\r
299  */\r
300 \r
301 void CompEntryInit(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
302 {\r
303         RtlZeroMemory(pEntry, sizeof *pEntry);\r
304         pEntry->Channel = pChannel;\r
305 }\r
306 \r
307 DWORD CompEntryPost(COMP_ENTRY *pEntry)\r
308 {\r
309         if (InterlockedCompareExchange(&pEntry->Busy, 1, 0) == 0) {\r
310                 if (!PostQueuedCompletionStatus(pEntry->Channel->Manager->CompQueue,\r
311                                                                                 0, 0, &pEntry->Overlap)) {\r
312                         InterlockedExchange(&pEntry->Busy, 0);\r
313                         return GetLastError();\r
314                 }\r
315         }\r
316         return 0;\r
317 }\r
318 \r
319 COMP_ENTRY *CompEntryCancel(COMP_ENTRY *pEntry)\r
320 {\r
321         COMP_ENTRY *entry = NULL;\r
322 \r
323         while (pEntry->Busy) {\r
324                 Sleep(0);\r
325                 entry = CompChannelFindRemove(pEntry->Channel, pEntry);\r
326         }\r
327         return entry;\r
328 }\r