Implemented CoReleaseMarshalData.
[wine] / dlls / netapi32 / nbcmdqueue.c
1 /* Copyright (c) 2003 Juan Lang
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 #include "config.h"
18 #include "wine/debug.h"
19 #include "nbcmdqueue.h"
20
21 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
22
23 struct NBCmdQueue
24 {
25     HANDLE           heap;
26     CRITICAL_SECTION cs;
27     PNCB             head;
28 };
29
30 #define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve)
31 #define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE))
32
33 /* The reserved area of an ncb will be used for the following data:
34  * - a cancelled flag (BOOL, 4 bytes??)
35  * - a handle to an event that's set by a cancelled command on completion
36  *   (HANDLE, 4 bytes)
37  * These members are used in the following way
38  * - on cancel, set the event member of the reserved field (with create event)
39  * - NBCmdComplete will delete the ncb from the queue of there's no event;
40  *   otherwise it will set the event and not delete the ncb
41  * - cancel must lock the queue before finding the ncb in it, and can unlock it
42  *   once it's set the event (and the cancelled flag)
43  * - NBCmdComplete must lock the queue before attempting to remove the ncb or
44  *   check the event
45  * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue.
46  *   It'll then unlock the queue, and wait on the event in the head of the queue
47  *   until there's no more ncb's in the queue.
48  * Space optimization: use the handle as a boolean.  NULL == 0 => not cancelled.
49  * Non-NULL == valid handle => cancelled.  This allows storing a next pointer
50  * in the ncb's reserved field as well, avoiding a memory alloc for a new
51  * command (cool).
52  */
53
54 struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap)
55 {
56     struct NBCmdQueue *queue;
57
58     if (heap == NULL)
59         heap = GetProcessHeap();
60     queue = (struct NBCmdQueue *)HeapAlloc(heap, 0, sizeof(struct NBCmdQueue));
61     if (queue)
62     {
63         queue->heap = heap;
64         InitializeCriticalSection(&queue->cs);
65         queue->head = NULL;
66     }
67     return queue;
68 }
69
70 UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb)
71 {
72     UCHAR ret;
73
74     TRACE(": queue %p, ncb %p\n", queue, ncb);
75
76     if (!queue)
77         return NRC_BADDR;
78     if (!ncb)
79         return NRC_INVADDRESS;
80
81     *CANCEL_EVENT_PTR(ncb) = NULL;
82     EnterCriticalSection(&queue->cs);
83     *NEXT_PTR(ncb) = queue->head;
84     queue->head = ncb;
85     ret = NRC_GOODRET;
86     LeaveCriticalSection(&queue->cs);
87     TRACE("returning 0x%02x\n", ret);
88     return ret;
89 }
90
91 static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb)
92 {
93     PNCB *ret;
94
95     if (!queue || !ncb)
96         ret = NULL;
97     else
98     {
99         ret = &queue->head;
100         while (ret && *ret != ncb)
101             ret = NEXT_PTR(*ret);
102     }
103     return ret;
104 }
105
106 UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb)
107 {
108     UCHAR ret;
109     PNCB *spot;
110
111     TRACE(": queue %p, ncb %p\n", queue, ncb);
112
113     if (!queue)
114         return NRC_BADDR;
115     if (!ncb)
116         return NRC_INVADDRESS;
117
118     EnterCriticalSection(&queue->cs);
119     spot = NBCmdQueueFindNBC(queue, ncb);
120     if (spot)
121     {
122         *CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL);
123         WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE);
124         CloseHandle(*CANCEL_EVENT_PTR(*spot));
125         *spot = *NEXT_PTR(*spot);
126         if (ncb->ncb_retcode == NRC_CMDCAN)
127             ret = NRC_CMDCAN;
128         else
129             ret = NRC_CANOCCR;
130     }
131     else
132         ret = NRC_INVADDRESS;
133     LeaveCriticalSection(&queue->cs);
134     TRACE("returning 0x%02x\n", ret);
135     return ret;
136 }
137
138 UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode)
139 {
140     UCHAR ret;
141     PNCB *spot;
142
143     TRACE(": queue %p, ncb %p\n", queue, ncb);
144
145     if (!queue)
146         return NRC_BADDR;
147     if (!ncb)
148         return NRC_INVADDRESS;
149
150     EnterCriticalSection(&queue->cs);
151     spot = NBCmdQueueFindNBC(queue, ncb);
152     if (spot)
153     {
154         if (*CANCEL_EVENT_PTR(*spot))
155             SetEvent(*CANCEL_EVENT_PTR(*spot));
156         else
157             *spot = *NEXT_PTR(*spot);
158         ret = NRC_GOODRET;
159     }
160     else
161         ret = NRC_INVADDRESS;
162     LeaveCriticalSection(&queue->cs);
163     TRACE("returning 0x%02x\n", ret);
164     return ret;
165 }
166
167 UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue)
168 {
169     UCHAR ret;
170
171     TRACE(": queue %p\n", queue);
172
173     if (!queue)
174         return NRC_BADDR;
175
176     EnterCriticalSection(&queue->cs);
177     while (queue->head)
178     {
179         TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head,
180          queue->head->ncb_command);
181         NBCmdQueueCancel(queue, queue->head);
182     }
183     LeaveCriticalSection(&queue->cs);
184     ret = NRC_GOODRET;
185     TRACE("returning 0x%02x\n", ret);
186     return ret;
187 }
188
189 void NBCmdQueueDestroy(struct NBCmdQueue *queue)
190 {
191     TRACE(": queue %p\n", queue);
192
193     if (queue)
194     {
195         NBCmdQueueCancelAll(queue);
196         DeleteCriticalSection(&queue->cs);
197         HeapFree(queue->heap, 0, queue);
198     }
199 }