task_api void uninit_lib() { if( InterlockedExchangeAdd(&ref_count, -1) == 1 ){ DeleteCriticalSection(&(task_list_lock)); } }
static void* alloc_from_fragment (Fragment *frag, size_t size) { char *p = frag->fragment_next; char *end = p + size; if (end > frag->fragment_end) return NULL; /* p = frag->fragment_next must happen before */ mono_memory_barrier (); if (InterlockedCompareExchangePointer ((volatile gpointer*)&frag->fragment_next, end, p) != p) return NULL; if (frag->fragment_end - end < SGEN_MAX_NURSERY_WASTE) { Fragment *next, **prev_ptr; /* * Before we clean the remaining nursery, we must claim the remaining space * as it could end up been used by the range allocator since it can end up * allocating from this dying fragment as it doesn't respect SGEN_MAX_NURSERY_WASTE * when doing second chance allocation. */ if (mono_sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION && claim_remaining_size (frag, end)) { /* Clear the remaining space, pinning depends on this. FIXME move this to use phony arrays */ memset (end, 0, frag->fragment_end - end); HEAVY_STAT (InterlockedExchangeAdd (&stat_wasted_bytes_trailer, frag->fragment_end - end)); #ifdef NALLOC_DEBUG add_alloc_record (end, frag->fragment_end - end, BLOCK_ZEROING); #endif } prev_ptr = find_previous_pointer_fragment (frag); /*Use Michaels linked list remove*/ /*prev_ptr will be null is the fragment was removed concurrently */ while (prev_ptr) { next = frag->next; /*already deleted*/ if (!get_mark (next)) { /*frag->next read must happen before the first CAS*/ mono_memory_write_barrier (); /*Fail if the next done is removed concurrently and its CAS wins */ if (InterlockedCompareExchangePointer ((volatile gpointer*)&frag->next, mask (next, 1), next) != next) { continue; } } /* The second CAS must happen after the first CAS or frag->next. */ mono_memory_write_barrier (); /* Fail if the previous node was deleted and its CAS wins */ if (InterlockedCompareExchangePointer ((volatile gpointer*)prev_ptr, next, frag) != frag) { prev_ptr = find_previous_pointer_fragment (frag); continue; } /* No need to membar here since the worst that can happen is a CAS failure. */ do { frag->next_free = fragment_freelist; } while (InterlockedCompareExchangePointer ((volatile gpointer*)&fragment_freelist, frag, frag->next_free) != frag->next_free); break; } } return p; }
/*** Nursery memory allocation ***/ void mono_sgen_nursery_retire_region (void *address, ptrdiff_t size) { HEAVY_STAT (InterlockedExchangeAdd (&stat_wasted_bytes_discarded_fragments, size)); }
static NTSTATUS V4vCtrlBind(XENV4V_EXTENSION *pde, XENV4V_CONTEXT *ctx, V4V_BIND_VALUES *bvs) { NTSTATUS status = STATUS_SUCCESS; LONG val; KLOCK_QUEUE_HANDLE lqh; XENV4V_RING *robj; uint32_t port; // Use a simple guard variable to enforce the state transition order val = InterlockedExchangeAdd(&ctx->state, 0); if (val != XENV4V_STATE_IDLE) { TraceWarning(("state not IDLE, cannot complete bind request\n")); return STATUS_INVALID_DEVICE_REQUEST; } ASSERT(ctx->ringObject == NULL); do { if ((bvs->ringId.addr.domain != V4V_DOMID_NONE)&& (bvs->ringId.addr.domain != DOMID_INVALID_COMPAT)) { TraceWarning(("failure - ring ID domain must be V4V_DOMID_NONE - value: 0x%x\n", bvs->ringId.addr.domain)); status = STATUS_INVALID_PARAMETER; break; } robj = V4vAllocateRing(ctx->ringLength); if (robj == NULL) { TraceError(("failed to allocate the ring\n")); status = STATUS_NO_MEMORY; break; } robj->ring->id = bvs->ringId; // Have to grab this outside of lock at IRQL PASSIVE port = V4vRandomPort(pde); // Lock this section since we access the list KeAcquireInStackQueuedSpinLock(&pde->ringLock, &lqh); if (robj->ring->id.addr.port == V4V_PORT_NONE) { robj->ring->id.addr.port = V4vSparePortNumber(pde, port); } else if (V4vRingIdInUse(pde, &robj->ring->id)) { KeReleaseInStackQueuedSpinLock(&lqh); TraceWarning(("ring ID already in use, cannot bind\n")); status = STATUS_INVALID_DEVICE_REQUEST; break; } // Now register the ring. status = V4vRegisterRing(robj); if (!NT_SUCCESS(status)) { KeReleaseInStackQueuedSpinLock(&lqh); TraceError(("failed in register ring hypercall - error: 0x%x\n", status)); break; } robj->registered = TRUE; // Link it to the main list and set our pointer to it V4vLinkToRingList(pde, robj); ctx->ringObject = robj; KeReleaseInStackQueuedSpinLock(&lqh); InterlockedExchange(&ctx->type, XENV4V_TYPE_DATAGRAM); InterlockedExchange(&ctx->state, XENV4V_STATE_BOUND); } while (FALSE); if (!NT_SUCCESS(status)) { // If it failed, undo everything - this will remove it from the list if (ctx->ringObject != NULL) { V4vReleaseRing(pde, ctx->ringObject); } } return status; }
NTSTATUS NTAPI V4vDispatchDeviceControl(PDEVICE_OBJECT fdo, PIRP irp) { NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION isl; ULONG ioControlCode; PVOID ioBuffer; ULONG ioInLen; ULONG ioOutLen; XENV4V_EXTENSION *pde = V4vGetDeviceExtension(fdo); XENV4V_CONTEXT *ctx; LONG ds; TraceVerbose(("====> '%s'.\n", __FUNCTION__)); isl = IoGetCurrentIrpStackLocation(irp); ioControlCode = isl->Parameters.DeviceIoControl.IoControlCode; ioBuffer = irp->AssociatedIrp.SystemBuffer; ioInLen = isl->Parameters.DeviceIoControl.InputBufferLength; ioOutLen = isl->Parameters.DeviceIoControl.OutputBufferLength; ctx = (XENV4V_CONTEXT*)isl->FileObject->FsContext; TraceVerbose((" =IOCTL= 0x%x\n", ioControlCode)); irp->IoStatus.Information = 0; ds = InterlockedExchangeAdd(&pde->state, 0); if (ds & XENV4V_DEV_STOPPED) { TraceVerbose(("aborting IOCTL IRP, device is in the stopped state.\n")); irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; IoCompleteRequest(irp, IO_NO_INCREMENT); TraceVerbose(("<==== '%s'.\n", __FUNCTION__)); return STATUS_INVALID_DEVICE_STATE; } switch (ioControlCode) { #if defined(_WIN64) case V4V_IOCTL_INITIALIZE_32: { V4V_INIT_VALUES_32 *invs32 = (V4V_INIT_VALUES_32*)ioBuffer; if (ioInLen == sizeof(V4V_INIT_VALUES_32)) { V4V_INIT_VALUES init; init.rxEvent = invs32->rxEvent; init.ringLength = invs32->ringLength; status = V4vCtrlInitializeFile(ctx, &init, irp); } else { TraceError(("invalid initialization values.\n")); status = STATUS_INVALID_PARAMETER; } break; } #endif case V4V_IOCTL_INITIALIZE: { V4V_INIT_VALUES *invs = (V4V_INIT_VALUES*)ioBuffer; if (ioInLen == sizeof(V4V_INIT_VALUES)) { status = V4vCtrlInitializeFile(ctx, invs, irp); } else { TraceError(("invalid initialization values.\n")); status = STATUS_INVALID_PARAMETER; } break; } case V4V_IOCTL_BIND: { V4V_BIND_VALUES *bvs = (V4V_BIND_VALUES*)ioBuffer; if (ioInLen == sizeof(V4V_BIND_VALUES)) { status = V4vCtrlBind(pde, ctx, bvs); } else { TraceError(("invalid bind values.\n")); status = STATUS_INVALID_PARAMETER; } break; } case V4V_IOCTL_LISTEN: { V4V_LISTEN_VALUES *lvs = (V4V_LISTEN_VALUES*)ioBuffer; if (ioInLen == sizeof(V4V_LISTEN_VALUES)) { status = V4vCtrlListen(ctx, lvs); } else { TraceError(("invalid listen values.\n")); status = STATUS_INVALID_PARAMETER; } break; } #if defined(_WIN64) case V4V_IOCTL_ACCEPT_32: // Fall through #endif case V4V_IOCTL_ACCEPT: { status = V4vCtrlAccept(pde, ctx, ioControlCode, ioBuffer, ioInLen, irp); break; } case V4V_IOCTL_CONNECT: { V4V_CONNECT_VALUES *cvs = (V4V_CONNECT_VALUES*)ioBuffer; if (ioInLen == sizeof(V4V_CONNECT_VALUES)) { status = V4vCtrlConnect(pde, ctx, cvs, irp); } else { TraceError(("invalid connect values.\n")); status = STATUS_INVALID_PARAMETER; } break; } case V4V_IOCTL_WAIT: { V4V_WAIT_VALUES *wvs = (V4V_WAIT_VALUES*)ioBuffer; if (ioInLen == sizeof(V4V_WAIT_VALUES)) { status = V4vCtrlConnectWait(pde, ctx, wvs, irp); } else { TraceError(("invalid connect wait values.\n")); status = STATUS_INVALID_PARAMETER; } break; } case V4V_IOCTL_DISCONNECT: { status = V4vCtrlDisconnect(pde, ctx); break; } case V4V_IOCTL_GETINFO: { V4V_GETINFO_VALUES *gi = (V4V_GETINFO_VALUES*)ioBuffer; if (ioInLen == sizeof(V4V_GETINFO_VALUES)) { status = V4vCtrlGetInfo(ctx, gi); } else { TraceError(("invalid get info values.\n")); status = STATUS_INVALID_PARAMETER; } if (NT_SUCCESS(status)) { irp->IoStatus.Information = sizeof(V4V_GETINFO_VALUES); } break; } case V4V_IOCTL_DUMPRING: { status = V4vCtrlDumpRing(ctx); break; } default: status = STATUS_INVALID_PARAMETER; } if (status != STATUS_PENDING) { irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } TraceVerbose(("<==== '%s'.\n", __FUNCTION__)); return status; }
int SemOp(int semid, struct sembuf *semopbuf, DWORD procid, int piid, IPCT *ipct) { int i; BOOL ret; DWORD dwret; HANDLE hsem; #ifndef TERMINAL_SERVICE char semstr[16]; #else char semstr[30]; #endif /* TERMINAL_SERVICE */ #ifndef TERMINAL_SERVICE MakeSemstr(semstr, semopbuf->sem_num, semid, ipct->semt[semid].key); #else OSVERSIONINFOEX osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if( !GetVersionEx ((OSVERSIONINFO *) &osvi) ) { // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); GetVersionEx( (OSVERSIONINFO *) &osvi ); } if( osvi.dwMajorVersion >= 5 ) /* Windows 2000 */ MakeGlobalSemstr(semstr, semopbuf->sem_num, semid, ipct->semt[semid].key); else MakeSemstr(semstr, semopbuf->sem_num, semid, ipct->semt[semid].key); #endif /* TERMINAL_SERVICE */ hsem=OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, semstr); if (hsem==NULL) { errno=GetLastError(); return -1; } if (semopbuf->sem_op<0) { if (ipct->semb[semid][semopbuf->sem_num].semval >= abs((int)semopbuf->sem_op)) ipct->semb[semid][semopbuf->sem_num].semncnt++; if (ipct->semb[semid][semopbuf->sem_num].semval < abs((int)semopbuf->sem_op)) { if (semopbuf->sem_flg & IPC_NOWAIT) { ipct->semb[semid][semopbuf->sem_num].sempid=procid; CloseHandle(hsem); return 0; } } for (i=0; i<abs((int)semopbuf->sem_op); i++) { /************** do { dwret=MsgWaitForMultipleObjects(1, &hsem, FALSE, INFINITE, QS_SENDMESSAGE|QS_POSTMESSAGE|QS_TIMER); ***************/ dwret=WaitForSingleObject(hsem, INFINITE); switch (dwret) { /***************** case WAIT_OBJECT_0 + 1 : if (l_peekmessage()<0) { CloseHandle(hsem); return -1; } break; *****************/ case WAIT_OBJECT_0 : /* by KJC 98.09.24 ************************************** ipct->semb[semid][semopbuf->sem_num].semval--; ***********************************************************/ InterlockedDecrement(&ipct->semb[semid][semopbuf->sem_num].semval); /***********************************************************/ break; case WAIT_TIMEOUT : case WAIT_FAILED : errno=GetLastError(); default : CloseHandle(hsem); return -1; } /*************** } while (dwret==(WAIT_OBJECT_0 + 1)); ***************/ if (semopbuf->sem_flg & SEM_UNDO) { ipct->pseminfo[piid].semadj[semid][semopbuf->sem_num]++; } } ipct->semb[semid][semopbuf->sem_num].sempid=procid; ipct->semb[semid][semopbuf->sem_num].semncnt--; // CloseHandle(hsem); } else if (semopbuf->sem_op>0) { ret=ReleaseSemaphore(hsem, semopbuf->sem_op, NULL); if (ret==FALSE) { CloseHandle(hsem); return -1; } ipct->semb[semid][semopbuf->sem_num].sempid=procid; /* by KJC 98.9.24 ******************************** ipct->semb[semid][semopbuf->sem_num].semval+=(int)semopbuf->sem_op; ***********************************************************/ InterlockedExchangeAdd(&ipct->semb[semid][semopbuf->sem_num].semval, (long)semopbuf->sem_op); /***********************************************************/ // CloseHandle(hsem); if (semopbuf->sem_flg & SEM_UNDO) { ipct->pseminfo[piid].semadj[semid][semopbuf->sem_num]-=(int)semopbuf->sem_op; } } else { if (!ipct->semb[semid][semopbuf->sem_num].semval) { CloseHandle(hsem); return 0; } if (semopbuf->sem_flg & IPC_NOWAIT) { CloseHandle(hsem); return 0; } ipct->semb[semid][semopbuf->sem_num].sempid=procid; ipct->semb[semid][semopbuf->sem_num].semzcnt++; /*###################################*/ /*###################################*/ } CloseHandle(hsem); return 0; }
static NTSTATUS V4vCtrlAccept(XENV4V_EXTENSION *pde, XENV4V_CONTEXT *ctx, ULONG ioc, VOID *iob, ULONG iol, PIRP irp) { NTSTATUS status = STATUS_SUCCESS; LONG val; V4V_INIT_VALUES init; FILE_OBJECT *pfo = NULL; XENV4V_CONTEXT *actx; XENV4V_INSERT ins = {FALSE}; HANDLE fh; HANDLE rxe; V4V_ACCEPT_PRIVATE *priv; val = InterlockedExchangeAdd(&ctx->state, 0); if (val != XENV4V_STATE_LISTENING) { TraceWarning(("state not LISTENING, cannot complete accept request\n")); return STATUS_INVALID_DEVICE_REQUEST; } // Handle 32b/64b thunk sructures here and test input #if defined(_WIN64) if (ioc == V4V_IOCTL_ACCEPT_32) { V4V_ACCEPT_VALUES_32 *avs32 = (V4V_ACCEPT_VALUES_32*)iob; if (iol != sizeof(V4V_ACCEPT_VALUES_32)) { TraceError(("invalid accept values.\n")); return STATUS_INVALID_PARAMETER; } fh = avs32->fileHandle; rxe = avs32->rxEvent; priv = (V4V_ACCEPT_PRIVATE*)((UCHAR*)avs32 + FIELD_OFFSET(V4V_ACCEPT_VALUES_32, priv)); } else #endif { V4V_ACCEPT_VALUES *avs = (V4V_ACCEPT_VALUES*)iob; UNREFERENCED_PARAMETER(ioc); if (iol != sizeof(V4V_ACCEPT_VALUES)) { TraceError(("invalid accept values.\n")); return STATUS_INVALID_PARAMETER; } fh = avs->fileHandle; rxe = avs->rxEvent; priv = (V4V_ACCEPT_PRIVATE*)((UCHAR*)avs + FIELD_OFFSET(V4V_ACCEPT_VALUES, priv)); } // Any IRPs that are queued are given a sanity initialization V4vInitializeIrp(irp); // Get a reference to the file object for the handle status = ObReferenceObjectByHandle(fh, 0, *IoFileObjectType, irp->RequestorMode, &pfo, NULL); if (!NT_SUCCESS(status)) { TraceError(("failed to get a reference to the accepter file object - error: 0x%x\n", status)); return status; } actx = (XENV4V_CONTEXT*)pfo->FsContext; ObDereferenceObject(pfo); // Store the referenced acceptor context in the IOCTL buffer so we can access it at > PASSIVE later. V4vAddRefContext(pde, actx); #if defined(_WIN64) priv->q.a = (ULONG64)actx; #else priv->d.a = (ULONG32)actx; #endif // Do the base initialization of the file object context init.rxEvent = rxe; init.ringLength = ctx->ringLength; // shared ring length status = V4vCtrlInitializeFile(actx, &init, irp); if (!NT_SUCCESS(status)) { V4vReleaseContext(pde, actx); TraceError(("failed to initialize the accepter file object - error: 0x%x\n", status)); return status; } // Now initialize the accepter specific state and associate the accepter // with the listener context and ring. KeInitializeSpinLock(&actx->u.accepter.dataLock); actx->u.accepter.dataList = NULL; actx->u.accepter.dataTail = NULL; V4vAddRefContext(pde, ctx); V4vAddRefRing(pde, ctx->ringObject); actx->u.accepter.listenerContext = ctx; actx->ringObject = ctx->ringObject; // Now it becomes an accepter type for ever more InterlockedExchange(&actx->type, XENV4V_TYPE_ACCEPTER); // After this transition, we will wait for a SYN (may be one in the queue already). InterlockedExchange(&actx->state, XENV4V_STATE_ACCEPTING); // Flag it irp->Tail.Overlay.DriverContext[0] = (PVOID)(ULONG_PTR)(XENV4V_PEEK_STREAM|XENV4V_PEEK_ACCEPT|XENV4V_PEEK_IOCTL); // Always queue it to the back and marks it pending. If it fails to be queued then // the user mode call will close the new handle. status = IoCsqInsertIrpEx(&pde->csqObject, irp, NULL, &ins); if (NT_SUCCESS(status)) { status = STATUS_PENDING; // Drive any accepts V4vDoAccepts(pde, ctx); } return status; }
// Write procedure of the device NTSTATUS SlDeviceWriteProc(DEVICE_OBJECT *device_object, IRP *irp) { SL_DEVICE *dev = *((SL_DEVICE **)device_object->DeviceExtension); NTSTATUS ret = STATUS_UNSUCCESSFUL; IO_STACK_LOCATION *irp_stack = IoGetCurrentIrpStackLocation(irp); UINT ret_size = 0; if (dev->IsBasicDevice == false) { // Adapter device SL_FILE *f = irp_stack->FileObject->FsContext; if (irp_stack->Parameters.Write.Length == SL_EXCHANGE_BUFFER_SIZE) { UCHAR *buf = irp->UserBuffer; if (dev->Halting || dev->Adapter->Halt || buf == NULL) { // Halting } else { // Write the packet MDL *mdl; UINT num = SL_NUM_PACKET(buf); mdl = IoAllocateMdl(buf, SL_EXCHANGE_BUFFER_SIZE, false, false, NULL); if (mdl != NULL) { MmProbeAndLockPages(mdl, KernelMode, IoReadAccess); } ret = true; ret_size = SL_EXCHANGE_BUFFER_SIZE; if (num >= 1 && num <= SL_MAX_PACKET_EXCHANGE) { UINT i, j; NET_BUFFER_LIST *nbl_head = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_packets = 0; NDIS_HANDLE adapter_handle = NULL; SlLock(f->Adapter->Lock); if (f->Adapter->NumPendingSendPackets <= SL_MAX_PACKET_QUEUED) { // Admit to send only if the number of packets being transmitted does not exceed the specified limit adapter_handle = f->Adapter->AdapterHandle; } if (adapter_handle != NULL) { // Lock the file list which opens the same adapter SlLockList(dev->FileList); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Lock the receive queue of other file lists SlLock(other->RecvLock); other->SetEventFlag = false; } } for (i = 0;i < num;i++) { UINT packet_size = SL_SIZE_OF_PACKET(buf, i); UCHAR *packet_buf; NET_BUFFER_LIST *nbl = NULL; bool ok = false; if (packet_size > SL_MAX_PACKET_SIZE) { packet_size = SL_MAX_PACKET_SIZE; } else if (packet_size < SL_PACKET_HEADER_SIZE) { packet_size = SL_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)SL_ADDR_OF_PACKET(buf, i); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Insert into the receive queue of the other file lists if (other->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); SlCopy(q->Data, packet_buf, packet_size); q->Size = packet_size; q->Next = NULL; if (other->RecvPacketHead == NULL) { other->RecvPacketHead = q; } else { other->RecvPacketTail->Next = q; } other->RecvPacketTail = q; other->NumRecvPackets++; other->SetEventFlag = true; } } } // Allocate a new NET_BUFFER_LIST if (f->NetBufferListPool != NULL) { nbl = NdisAllocateNetBufferList(f->NetBufferListPool, 16, 0); if (nbl != NULL) { nbl->SourceHandle = adapter_handle; } } if (nbl != NULL) { // Get the NET_BUFFER from the NET_BUFFER_LIST NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; if (nb != NULL && OK(NdisRetreatNetBufferDataStart(nb, packet_size, 0, NULL))) { // Buffer copy UCHAR *dst = NdisGetDataBuffer(nb, packet_size, NULL, 1, 0); if (dst != NULL) { SlCopy(dst, packet_buf, packet_size); ok = true; } else { NdisAdvanceNetBufferDataStart(nb, packet_size, false, NULL); } } } if (ok == false) { if (nbl != NULL) { NdisFreeNetBufferList(nbl); } } else { if (nbl_head == NULL) { nbl_head = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)) = f; num_packets++; } } for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Release the receive queue of other file lists SlUnlock(other->RecvLock); // Set an event if (other->SetEventFlag) { SlSet(other->Event); } } } SlUnlockList(dev->FileList); if (nbl_head != NULL) { InterlockedExchangeAdd(&f->NumSendingPacketets, num_packets); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, num_packets); SlUnlock(f->Adapter->Lock); NdisSendNetBufferLists(adapter_handle, nbl_head, 0, 0); } else { SlUnlock(f->Adapter->Lock); } } else { SlUnlock(f->Adapter->Lock); } } if (mdl != NULL) { MmUnlockPages(mdl); IoFreeMdl(mdl); } } } } irp->IoStatus.Information = ret_size; irp->IoStatus.Status = ret; IoCompleteRequest(irp, IO_NO_INCREMENT); return ret; }
atomic_t atomicExchangeAdd(volatile atomic_t& value, atomic_t n) { return InterlockedExchangeAdd(const_cast<atomic_t*>(&value), n); }
INLINE INT32 interlocked_add(INT32 volatile *ptr, INT32 add) { return InterlockedExchangeAdd((LPLONG)ptr, add) + add; }
int32_t atom_add(int32_t *dest, int32_t incr) { return InterlockedExchangeAdd((LONG *)dest, incr) + incr; }
long juce_InterlockedExchangeAdd (volatile long* a, long b) noexcept { return InterlockedExchangeAdd (a, b); }
int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock) { *ret = InterlockedExchangeAdd(val, amount) + amount; return 1; }
static WorkItemState GetWorkItemState (EncryptionThreadPoolWorkItem *workItem) { return InterlockedExchangeAdd ((LONG *) &workItem->State, 0); }