winverbs/nd: do not convert timeout status value
[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 CompChannelQueue(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry);\r
34 \r
35 \r
36 /*\r
37  * Completion manager\r
38  */\r
39 \r
40 static unsigned __stdcall CompThreadPoll(void *Context)\r
41 {\r
42         COMP_MANAGER *mgr = (COMP_MANAGER *) Context;\r
43         COMP_ENTRY *entry;\r
44         OVERLAPPED *overlap;\r
45         DWORD bytes;\r
46         ULONG_PTR key;\r
47 \r
48         while (mgr->Run) {\r
49                 GetQueuedCompletionStatus(mgr->CompQueue, &bytes, &key,\r
50                                                                   &overlap, INFINITE);\r
51                 entry = CONTAINING_RECORD(overlap, COMP_ENTRY, Overlap);\r
52                 if (entry->Channel != NULL) {\r
53                         CompChannelQueue(entry->Channel, entry);\r
54                 }\r
55         }\r
56 \r
57         _endthreadex(0);\r
58         return 0;\r
59 }\r
60 \r
61 DWORD CompManagerOpen(COMP_MANAGER *pMgr)\r
62 {\r
63         DWORD ret;\r
64 \r
65         pMgr->CompQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, -1);\r
66         if (pMgr->CompQueue == NULL) {\r
67                 return GetLastError();\r
68         }\r
69 \r
70         pMgr->Run = TRUE;\r
71         pMgr->Thread = (HANDLE) _beginthreadex(NULL, 0, CompThreadPoll, pMgr, 0, NULL);\r
72         if (pMgr->Thread == NULL) {\r
73                 ret = GetLastError();\r
74                 goto err;\r
75         }\r
76         return 0;\r
77 \r
78 err:\r
79         CloseHandle(pMgr->CompQueue);\r
80         return ret;\r
81 }\r
82 \r
83 void CompManagerClose(COMP_MANAGER *pMgr)\r
84 {\r
85         COMP_CHANNEL *channel;\r
86         COMP_ENTRY entry;\r
87 \r
88         pMgr->Run = FALSE;\r
89         CompEntryInit(NULL, &entry);\r
90         PostQueuedCompletionStatus(pMgr->CompQueue, 0, (ULONG_PTR) pMgr, &entry.Overlap);\r
91         WaitForSingleObject(pMgr->Thread, INFINITE);\r
92         CloseHandle(pMgr->Thread);\r
93 \r
94         CloseHandle(pMgr->CompQueue);\r
95 }\r
96 \r
97 DWORD CompManagerMonitor(COMP_MANAGER *pMgr, HANDLE hFile, ULONG_PTR Key)\r
98 {\r
99         HANDLE cq;\r
100 \r
101         cq = CreateIoCompletionPort(hFile, pMgr->CompQueue, Key, 0);\r
102         return (cq == NULL) ? GetLastError() : 0;\r
103 }\r
104 \r
105 \r
106 /*\r
107  * Completion channel sets\r
108  */\r
109 \r
110 DWORD CompSetInit(COMP_SET *pSet)\r
111 {\r
112         pSet->Head = NULL;\r
113         pSet->TailPtr = &pSet->Head;\r
114 \r
115         pSet->Event = CreateEvent(NULL, TRUE, FALSE, NULL);\r
116         if (pSet->Event == NULL) {\r
117                 return GetLastError();\r
118         }\r
119 \r
120         return 0;\r
121 }\r
122 \r
123 void CompSetCleanup(COMP_SET *pSet)\r
124 {\r
125         CloseHandle(pSet->Event);\r
126 }\r
127 \r
128 void CompSetZero(COMP_SET *pSet)\r
129 {\r
130         pSet->Head = NULL;\r
131         pSet->TailPtr = &pSet->Head;\r
132         ResetEvent(pSet->Event);\r
133 }\r
134 \r
135 void CompSetAdd(COMP_CHANNEL *pChannel, COMP_SET *pSet)\r
136 {\r
137         *pSet->TailPtr = pChannel;\r
138         pSet->TailPtr = &pChannel->Next;\r
139 \r
140         EnterCriticalSection(&pChannel->Lock);\r
141         pChannel->Set = pSet;\r
142         if (pChannel->Head != NULL) {\r
143                 SetEvent(pSet->Event);\r
144         }\r
145         LeaveCriticalSection(&pChannel->Lock);\r
146 }\r
147 \r
148 DWORD CompSetPoll(COMP_SET *pSet, DWORD Milliseconds)\r
149 {\r
150         DLIST_ENTRY *entry;\r
151         COMP_CHANNEL *channel;\r
152         DWORD ret, cnt = 0;\r
153 \r
154         *pSet->TailPtr = NULL;\r
155         ret = WaitForSingleObject(pSet->Event, Milliseconds);\r
156         if (ret == WAIT_TIMEOUT) {\r
157                 ret = 0;\r
158         }\r
159 \r
160         for (channel = pSet->Head; channel != NULL; channel = channel->Next) {\r
161                 EnterCriticalSection(&channel->Lock);\r
162                 channel->Set = NULL;\r
163                 cnt += (channel->Head != NULL);\r
164                 LeaveCriticalSection(&channel->Lock);\r
165         }\r
166 \r
167         return cnt ? cnt : ret;\r
168 }\r
169 \r
170 void CompSetCancel(COMP_SET *pSet)\r
171 {\r
172         SetEvent(pSet->Event);\r
173 }\r
174 \r
175 \r
176 /*\r
177  * Completion channel\r
178  */\r
179 \r
180 DWORD CompChannelInit(COMP_MANAGER *pMgr, COMP_CHANNEL *pChannel, DWORD Milliseconds)\r
181 {\r
182         pChannel->Manager = pMgr;\r
183         pChannel->Next = NULL;\r
184         pChannel->Set = NULL;\r
185         pChannel->Head = NULL;\r
186         pChannel->TailPtr = &pChannel->Head;\r
187         pChannel->Milliseconds = Milliseconds;\r
188 \r
189         pChannel->Event = CreateEvent(NULL, TRUE, TRUE, NULL);\r
190         if (pChannel->Event == NULL) {\r
191                 return GetLastError();\r
192         }\r
193 \r
194         InitializeCriticalSection(&pChannel->Lock);\r
195         CompEntryInit(pChannel, &pChannel->Entry);\r
196         return 0;\r
197 }\r
198 \r
199 void CompChannelCleanup(COMP_CHANNEL *pChannel)\r
200 {\r
201         CloseHandle(pChannel->Event);\r
202         DeleteCriticalSection(&pChannel->Lock); \r
203 }\r
204 \r
205 static void CompChannelInsertTail(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
206 {\r
207         *pChannel->TailPtr = pEntry;\r
208         pChannel->TailPtr = &pEntry->Next;\r
209         pEntry->Next = NULL;\r
210 }\r
211 \r
212 static COMP_ENTRY *CompChannelRemoveHead(COMP_CHANNEL *pChannel)\r
213 {\r
214         COMP_ENTRY *entry;\r
215 \r
216         entry = pChannel->Head;\r
217         pChannel->Head = entry->Next;\r
218         if (pChannel->Head == NULL) {\r
219                 pChannel->TailPtr = &pChannel->Head;\r
220         }\r
221         return entry;\r
222 }\r
223 \r
224 static COMP_ENTRY *CompChannelFindRemove(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
225 {\r
226         COMP_ENTRY **entry_ptr, *entry;\r
227 \r
228         EnterCriticalSection(&pChannel->Lock);\r
229         entry_ptr = &pChannel->Head;\r
230         while (*entry_ptr && *entry_ptr != pEntry) {\r
231                 entry_ptr = &(*entry_ptr)->Next;\r
232         }\r
233 \r
234         entry = *entry_ptr;\r
235         if (entry != NULL) {\r
236                 *entry_ptr = pEntry->Next;\r
237                 if (pChannel->TailPtr == &pEntry->Next) {\r
238                         pChannel->TailPtr = entry_ptr;\r
239                 }\r
240                 InterlockedExchange(&pEntry->Busy, 0);\r
241         }\r
242         LeaveCriticalSection(&pChannel->Lock);\r
243         return entry;\r
244 }\r
245 \r
246 static void CompChannelQueue(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
247 {\r
248         pEntry->Next = NULL;\r
249         EnterCriticalSection(&pChannel->Lock);\r
250         CompChannelInsertTail(pChannel, pEntry);\r
251         SetEvent(pChannel->Event);\r
252         if (pChannel->Set != NULL) {\r
253                 SetEvent(pChannel->Set->Event);\r
254         }\r
255         LeaveCriticalSection(&pChannel->Lock);\r
256 }\r
257 \r
258 DWORD CompChannelPoll(COMP_CHANNEL *pChannel, COMP_ENTRY **ppEntry)\r
259 {\r
260         COMP_ENTRY *entry;\r
261         DWORD ret;\r
262 \r
263         EnterCriticalSection(&pChannel->Lock);\r
264         while (pChannel->Head == NULL) {\r
265                 ResetEvent(pChannel->Event);\r
266                 LeaveCriticalSection(&pChannel->Lock);\r
267 \r
268                 ret = WaitForSingleObject(pChannel->Event, pChannel->Milliseconds);\r
269                 if (ret) {\r
270                         return ret;\r
271                 }\r
272 \r
273                 EnterCriticalSection(&pChannel->Lock);\r
274         }\r
275         entry = CompChannelRemoveHead(pChannel);\r
276         LeaveCriticalSection(&pChannel->Lock);\r
277 \r
278         InterlockedExchange(&entry->Busy, 0);\r
279         *ppEntry = entry;\r
280         ret = (entry == &pChannel->Entry) ? ERROR_CANCELLED : 0;\r
281 \r
282         return ret;\r
283 }\r
284 \r
285 void CompChannelCancel(COMP_CHANNEL *pChannel)\r
286 {\r
287         if (InterlockedCompareExchange(&pChannel->Entry.Busy, 1, 0) == 0) {\r
288                 PostQueuedCompletionStatus(pChannel->Manager->CompQueue, 0,\r
289                                                                    (ULONG_PTR) pChannel, &pChannel->Entry.Overlap);\r
290         }\r
291 }\r
292 \r
293 \r
294 /*\r
295  * Completion entry\r
296  */\r
297 \r
298 void CompEntryInit(COMP_CHANNEL *pChannel, COMP_ENTRY *pEntry)\r
299 {\r
300         RtlZeroMemory(pEntry, sizeof *pEntry);\r
301         pEntry->Channel = pChannel;\r
302 }\r
303 \r
304 DWORD CompEntryPost(COMP_ENTRY *pEntry)\r
305 {\r
306         if (InterlockedCompareExchange(&pEntry->Busy, 1, 0) == 0) {\r
307                 if (!PostQueuedCompletionStatus(pEntry->Channel->Manager->CompQueue,\r
308                                                                                 0, 0, &pEntry->Overlap)) {\r
309                         InterlockedExchange(&pEntry->Busy, 0);\r
310                         return GetLastError();\r
311                 }\r
312         }\r
313         return 0;\r
314 }\r
315 \r
316 COMP_ENTRY *CompEntryCancel(COMP_ENTRY *pEntry)\r
317 {\r
318         COMP_ENTRY *entry = NULL;\r
319 \r
320         while (pEntry->Busy) {\r
321                 Sleep(0);\r
322                 entry = CompChannelFindRemove(pEntry->Channel, pEntry);\r
323         }\r
324         return entry;\r
325 }\r