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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 16 */ 17 18 #include "netapi32.h" 19 20 WINE_DEFAULT_DEBUG_CHANNEL(netbios); 21 22 struct NBCmdQueue 23 { 24 HANDLE heap; 25 CRITICAL_SECTION cs; 26 PNCB head; 27 }; 28 29 #define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve) 30 #define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE)) 31 32 /* The reserved area of an ncb will be used for the following data: 33 * - a cancelled flag (BOOL, 4 bytes??) 34 * - a handle to an event that's set by a cancelled command on completion 35 * (HANDLE, 4 bytes) 36 * These members are used in the following way 37 * - on cancel, set the event member of the reserved field (with create event) 38 * - NBCmdComplete will delete the ncb from the queue of there's no event; 39 * otherwise it will set the event and not delete the ncb 40 * - cancel must lock the queue before finding the ncb in it, and can unlock it 41 * once it's set the event (and the cancelled flag) 42 * - NBCmdComplete must lock the queue before attempting to remove the ncb or 43 * check the event 44 * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue. 45 * It'll then unlock the queue, and wait on the event in the head of the queue 46 * until there's no more ncb's in the queue. 47 * Space optimization: use the handle as a boolean. NULL == 0 => not cancelled. 48 * Non-NULL == valid handle => cancelled. This allows storing a next pointer 49 * in the ncb's reserved field as well, avoiding a memory alloc for a new 50 * command (cool). 51 */ 52 53 struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap) 54 { 55 struct NBCmdQueue *queue; 56 57 if (heap == NULL) 58 heap = GetProcessHeap(); 59 queue = HeapAlloc(heap, 0, sizeof(struct NBCmdQueue)); 60 if (queue) 61 { 62 queue->heap = heap; 63 InitializeCriticalSection(&queue->cs); 64 queue->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NBCmdQueue.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 queue->cs.DebugInfo->Spare[0] = 0; 197 DeleteCriticalSection(&queue->cs); 198 HeapFree(queue->heap, 0, queue); 199 } 200 } 201