Status ModuleShutdown(volatile ModuleInitState* initState, void (*shutdown)()) { for(;;) { if(cpu_CAS(initState, INITIALIZED, BUSY)) { shutdown(); *initState = UNINITIALIZED; COMPILER_FENCE; return INFO::OK; } const ModuleInitState latchedInitState = *initState; if(latchedInitState == INITIALIZED || latchedInitState == BUSY) { cpu_Pause(); continue; } if(latchedInitState == UNINITIALIZED) return INFO::SKIPPED; ENSURE(latchedInitState < 0); return (Status)latchedInitState; } }
int pthread_key_create(pthread_key_t* key, void (*dtor)(void*)) { DWORD idx = TlsAlloc(); if(idx == TLS_OUT_OF_INDEXES) return -ENOMEM; ENSURE(idx < TLS_LIMIT); *key = (pthread_key_t)idx; // acquire a free dtor slot size_t i; for(i = 0; i < MAX_DTORS; i++) { if(cpu_CAS((volatile intptr_t*)&dtors[i].dtor, (intptr_t)0, (intptr_t)dtor)) goto have_slot; } // not enough slots; we have a valid key, but its dtor won't be called. WARN_IF_ERR(ERR::LIMIT); return -1; have_slot: dtors[i].key = *key; return 0; }
// NB: callers should skip this if *idxDeleterOut != 0 (avoids the overhead // of an unnecessary indirect function call) void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleterOut) { ENSURE(deleter); if(!cpu_CAS(idxDeleterOut, idxDeleterNone, -1)) // not the first call for this deleter { // wait until an index has been assigned while(*idxDeleterOut <= 0) cpu_Pause(); return; } const IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1); ENSURE(idxDeleter < (IdxDeleter)ARRAY_SIZE(deleters)); deleters[idxDeleter] = deleter; COMPILER_FENCE; *idxDeleterOut = idxDeleter; }
Status debug_WriteCrashlog(const wchar_t* text) { // (avoid infinite recursion and/or reentering this function if it // fails/reports an error) enum State { IDLE, BUSY, FAILED }; // note: the initial state is IDLE. we rely on zero-init because // initializing local static objects from constants may happen when // this is first called, which isn't thread-safe. (see C++ 6.7.4) cassert(IDLE == 0); static volatile intptr_t state; if(!cpu_CAS(&state, IDLE, BUSY)) return ERR::REENTERED; // NOWARN OsPath pathname = ah_get_log_dir()/"crashlog.txt"; FILE* f = sys_OpenFile(pathname, "w"); if(!f) { state = FAILED; // must come before DEBUG_DISPLAY_ERROR DEBUG_DISPLAY_ERROR(L"Unable to open crashlog.txt for writing (please ensure the log directory is writable)"); return ERR::FAIL; // NOWARN (the above text is more helpful than a generic error code) } fputwc(0xFEFF, f); // BOM fwprintf(f, L"%ls\n", text); fwprintf(f, L"\n\n====================================\n\n"); // allow user to bundle whatever information they want ah_bundle_logs(f); fclose(f); state = IDLE; return INFO::OK; }
Status ModuleInit(volatile ModuleInitState* initState, Status (*init)()) { for(;;) { if(cpu_CAS(initState, UNINITIALIZED, BUSY)) { Status ret = init(); *initState = (ret == INFO::OK)? INITIALIZED : ret; COMPILER_FENCE; return ret; } const ModuleInitState latchedInitState = *initState; if(latchedInitState == UNINITIALIZED || latchedInitState == BUSY) { cpu_Pause(); continue; } ENSURE(latchedInitState == INITIALIZED || latchedInitState < 0); return (Status)latchedInitState; } }
bool VfsDirectory::ShouldPopulate() { return cpu_CAS(&m_shouldPopulate, 1, 0); // test and reset }
void VfsDirectory::SetAssociatedDirectory(const PRealDirectory& realDirectory) { if(!cpu_CAS(&m_shouldPopulate, 0, 1)) DEBUG_WARN_ERR(ERR::LOGIC); // caller didn't check ShouldPopulate m_realDirectory = realDirectory; }
int pthread_once(pthread_once_t* once, void (*init_routine)()) { if(cpu_CAS((volatile intptr_t*)once, 0, 1)) init_routine(); return 0; }