// The following code also occurs in configure.ac, and both need to be // kept in sync.
#define OFFS 0x100 #define END (-1)
int cmpOpCode(unsignedchar *code, int *with) { int result = 0; while (*with >= 0) { if (*with == OFFS) {
result = *code;
} else { if (*code != *with) return -1;
}
code++;
with++;
} return result;
}
void FindTLSOffset() { // This is an idea borrowed from Mono. We test if the implementation // of pthread_getspecific() uses the assembly code below. If that is // true, we can replace calls to pthread_getspecific() with the // matching inline assembly, allowing a significant performance boost. // There are two possible implementations. staticint asm_code[] = { // movq %gs:[OFFS](,%rdi,8), %rax // retq 0x65, 0x48, 0x8b, 0x04, 0xfd, OFFS, 0x00, 0x00, 0x00, 0xc3, END
}; staticint asm_code2[] = { // pushq %rbp // movq %rsp, %rbp // movq %gs:[OFFS](,%rdi,8),%rax // popq %rbp // retq 0x55, 0x48, 0x89, 0xe5, 0x65, 0x48, 0x8b, 0x04, 0xfd, OFFS, 0x00, 0x00, 0x00, 0x5d, 0xc3, END
};
TLSOffset = cmpOpCode((unsignedchar *)pthread_getspecific, asm_code); if (TLSOffset >= 0) return;
TLSOffset = cmpOpCode((unsignedchar *)pthread_getspecific, asm_code2); if (TLSOffset >= 0) return;
Panic("Unable to find macOS thread-local storage offset");
} #endif
void FreeTLS(void * address)
{ /* We currently cannot free this memory because of garbage collector
* issues. Instead, it will be reused */ #if0
munmap(address, TLS_STACK_SIZE); #endif
}
/* In order to safely use thread-local memory on the main stack, we have *toworkaroundanidiosyncrasyinsomevirtualmemorysystems.These *VMimplementationsdonotallowfullyrandomaccesswithinthestack *segment,butonlyquasi-linearaccess:apagecanonlybeaccessedif *anearbypagewasaccessedbefore.Ifthispatternisnotobserved, *butifweaccessmemorywithinthestacksegmentrandomly--asin *ourTLSimplementation,butalsowithverylargestackframes--a *segmentationfaultcanarise.Toavoidsuchsegmentationfaults,we *traversethestacksegmentofthemainstack,touchingeachpagein *turn. * *Notethatthisisnotnecessaryforthreadstacks,whichare *allocatedinprivatememory-mappedstorage.
*/
static NOINLINE void GrowStack(void)
{ char * tls = (char *)GetTLS();
size_t pagesize = getpagesize(); /* p needs to be a volatile pointer so that the memory writes are not
* removed by the optimizer */ volatilechar * p = alloca(pagesize); while (p > tls) { // touch memory
*p = '\0';
p = alloca(pagesize);
}
} #endif
void RegionWriteLock(Region * region)
{ int result = 0;
assert(region->owner != GetTLS());
if (region->count_active || TLS(CountActive)) {
result = !pthread_rwlock_trywrlock(region->lock);
if (result) { if (region->count_active)
region->locks_acquired++; if (TLS(CountActive))
TLS(LocksAcquired)++;
} else { if (region->count_active)
region->locks_contended++; if (TLS(CountActive))
TLS(LocksContended)++;
pthread_rwlock_wrlock(region->lock);
}
} else {
pthread_rwlock_wrlock(region->lock);
}
region->owner = GetTLS();
}
int RegionTryWriteLock(Region * region)
{ int result;
assert(region->owner != GetTLS());
result = !pthread_rwlock_trywrlock(region->lock);
if (result) { if (region->count_active)
region->locks_acquired++; if (TLS(CountActive))
TLS(LocksAcquired)++;
region->owner = GetTLS();
} else { if (region->count_active)
region->locks_contended++; if (TLS(CountActive))
TLS(LocksContended)++;
} return result;
}
void RegionReadLock(Region * region)
{ int result = 0;
assert(region->owner != GetTLS());
assert(region->readers[TLS(threadID)] == 0);
if (region->count_active || TLS(CountActive)) {
result = !pthread_rwlock_rdlock(region->lock);
if (result) { if (region->count_active)
ATOMIC_INC(®ion->locks_acquired); if (TLS(CountActive))
TLS(LocksAcquired)++;
} else { if (region->count_active)
ATOMIC_INC(®ion->locks_contended); if (TLS(CountActive))
TLS(LocksAcquired)++;
pthread_rwlock_rdlock(region->lock);
}
} else {
pthread_rwlock_rdlock(region->lock);
}
region->readers[TLS(threadID)] = 1;
}
int RegionTryReadLock(Region * region)
{ int result = !pthread_rwlock_rdlock(region->lock);
assert(region->owner != GetTLS());
assert(region->readers[TLS(threadID)] == 0);
if (result) { if (region->count_active)
ATOMIC_INC(®ion->locks_acquired); if (TLS(CountActive))
TLS(LocksAcquired)++;
region->readers[TLS(threadID)] = 1;
} else { if (region->count_active)
ATOMIC_INC(®ion->locks_contended); if (TLS(CountActive))
TLS(LocksContended)++;
} return result;
}
LockStatus IsLocked(Region * region)
{ if (!region) return LOCK_STATUS_UNLOCKED; // public region if (region->owner == GetTLS()) return LOCK_STATUS_READWRITE_LOCKED; if (region->readers[TLS(threadID)]) return LOCK_STATUS_READONLY_LOCKED; return LOCK_STATUS_UNLOCKED;
}
Region * GetRegionOf(Obj obj)
{ if (!IS_BAG_REF(obj)) return NULL; if (TNUM_OBJ(obj) == T_REGION) return *(Region **)(ADDR_OBJ(obj)); return REGION(obj);
}
void SetRegionName(Region * region, Obj name)
{ if (name) { if (!IS_STRING(name)) return; if (IS_MUTABLE_OBJ(name))
name = CopyObj(name, 0);
}
MEMBAR_WRITE();
region->name = name;
}
Obj GetRegionName(Region * region)
{
Obj result; if (region)
result = region->name; else
result = PublicRegionName;
MEMBAR_READ(); return result;
}
void GetLockStatus(int count, Obj * objects, LockStatus * status)
{ int i; for (i = 0; i < count; i++)
status[i] = IsLocked(REGION(objects[i]));
}
void PopRegionLocks(int newSP)
{ while (newSP < TLS(lockStackPointer)) { int p = TLS(lockStackPointer)--;
Obj region_obj = ELM_PLIST(TLS(lockStack), p); if (!region_obj) continue;
Region * region = *(Region **)(ADDR_OBJ(region_obj));
RegionUnlock(region);
SET_ELM_PLIST(TLS(lockStack), p, (Obj)0);
}
}
int RegionLockSP(void)
{ return TLS(lockStackPointer);
}
int GetThreadState(int threadID)
{ return (int)(thread_data[threadID].state);
}
int UpdateThreadState(int threadID, int oldState, int newState)
{ return COMPARE_AND_SWAP(&thread_data[threadID].state,
(AtomicUInt)oldState, (AtomicUInt)newState);
}
staticint LockAndUpdateThreadState(int threadID, int oldState, int newState)
{ if (pthread_mutex_trylock(thread_data[threadID].lock) < 0) { return0;
} if (!UpdateThreadState(threadID, oldState, newState)) {
pthread_mutex_unlock(thread_data[threadID].lock); return0;
}
SetInterrupt(threadID);
pthread_cond_signal(thread_data[threadID].cond);
pthread_mutex_unlock(thread_data[threadID].lock); return1;
}
void PauseThread(int threadID)
{ for (;;) { int state = GetThreadState(threadID); switch (state & TSTATE_MASK) { case TSTATE_RUNNING: if (UpdateThreadState(threadID, TSTATE_RUNNING,
TSTATE_PAUSED |
(TLS(threadID) << TSTATE_SHIFT))) {
SetInterrupt(threadID); return;
} break; case TSTATE_BLOCKED: case TSTATE_SYSCALL: if (LockAndUpdateThreadState(threadID, state,
TSTATE_PAUSED |
(TLS(threadID) << TSTATE_SHIFT))) return; break; case TSTATE_PAUSED: case TSTATE_TERMINATED: case TSTATE_KILLED: case TSTATE_INTERRUPTED: return;
}
}
}
void HandleInterrupts(int locked, Stat stat)
{ switch (GetThreadState(TLS(threadID)) & TSTATE_MASK) { case TSTATE_PAUSED:
PauseCurrentThread(locked); break; case TSTATE_INTERRUPTED:
InterruptCurrentThread(locked, stat); break; case TSTATE_KILLED:
TerminateCurrentThread(locked); break;
}
}
void KillThread(int threadID)
{ for (;;) { int state = GetThreadState(threadID); switch (state & TSTATE_MASK) { case TSTATE_RUNNING: if (UpdateThreadState(threadID, TSTATE_RUNNING, TSTATE_KILLED)) {
SetInterrupt(threadID); return;
} break; case TSTATE_BLOCKED: if (LockAndUpdateThreadState(threadID, state, TSTATE_KILLED)) return; break; case TSTATE_SYSCALL: if (UpdateThreadState(threadID, state, TSTATE_KILLED)) { return;
} break; case TSTATE_INTERRUPTED: if (UpdateThreadState(threadID, state, TSTATE_KILLED)) {
SetInterrupt(threadID); return;
} break; case TSTATE_PAUSED: if (LockAndUpdateThreadState(threadID, state, TSTATE_KILLED)) { return;
} break; case TSTATE_TERMINATED: case TSTATE_KILLED: return;
}
}
}
void InterruptThread(int threadID, int handler)
{ for (;;) { int state = GetThreadState(threadID); switch (state & TSTATE_MASK) { case TSTATE_RUNNING: if (UpdateThreadState(threadID, TSTATE_RUNNING,
TSTATE_INTERRUPTED |
(handler << TSTATE_SHIFT))) {
SetInterrupt(threadID); return;
} break; case TSTATE_BLOCKED: if (LockAndUpdateThreadState(threadID, state,
TSTATE_INTERRUPTED |
(handler << TSTATE_SHIFT))) return; break; case TSTATE_SYSCALL: if (UpdateThreadState(threadID, state,
TSTATE_INTERRUPTED |
(handler << TSTATE_SHIFT))) return; break; case TSTATE_PAUSED: case TSTATE_TERMINATED: case TSTATE_KILLED: case TSTATE_INTERRUPTED: // We do not interrupt threads that are interrupted return;
}
}
}
void ResumeThread(int threadID)
{ int state = GetThreadState(threadID); if ((state & TSTATE_MASK) == TSTATE_PAUSED) {
LockAndUpdateThreadState(threadID, state, TSTATE_RUNNING);
}
}
int PauseAllThreads(void)
{ int i, n;
LockThreadControl(1); if (GlobalPauseInProgress) {
UnlockThreadControl(); return0;
}
GlobalPauseInProgress = 1; for (i = 0, n = 0; i < MAX_THREADS; i++) { switch (GetThreadState(i) & TSTATE_MASK) { case TSTATE_TERMINATED: case TSTATE_KILLED: break; default: if (!thread_data[i].system)
paused_threads[n++] = thread_data + i; break;
}
}
num_paused_threads = n;
UnlockThreadControl(); for (i = 0; i < n; i++)
PauseThread(i); return1;
}
void ResumeAllThreads(void)
{ int i, n;
n = num_paused_threads; for (i = 0; i < n; i++) {
ResumeThread(i);
}
}
staticInt CurrentRegionPrecedence(void)
{ Int sp; if (!DeadlockCheck || !TLS(lockStack)) return -1;
sp = TLS(lockStackPointer); while (sp > 0) {
Obj region_obj = ELM_PLIST(TLS(lockStack), sp); if (region_obj) { Int prec = ((Region *)(*ADDR_OBJ(region_obj)))->prec; if (prec >= 0) return prec;
}
sp--;
} return -1;
}
int LockObject(Obj obj, LockMode mode)
{
Region * region = GetRegionOf(obj); int result = TLS(lockStackPointer); if (!region) return result;
LockStatus locked = IsLocked(region); if (locked == LOCK_STATUS_READONLY_LOCKED && mode == LOCK_MODE_READWRITE) return -1; if (locked == LOCK_STATUS_UNLOCKED) { Int prec = CurrentRegionPrecedence(); if (prec >= 0 && region->prec >= prec && region->prec >= 0) return -1; if (region->fixed_owner) return -1; if (mode == LOCK_MODE_READWRITE)
RegionWriteLock(region); else
RegionReadLock(region);
PushRegionLock(region);
} return result;
}
int LockObjects(int count, Obj * objects, const LockMode * mode)
{ int result; int i, p; Int curr_prec;
LockRequest * order; if (count == 1) // fast path return LockObject(objects[0], mode[0]); if (count > MAX_LOCKS) return -1;
order = alloca(sizeof(LockRequest) * count); for (i = 0, p = 0; i < count; i++) {
Region * r = GetRegionOf(objects[i]); if (r) {
order[p].obj = objects[i];
order[p].region = GetRegionOf(objects[i]);
order[p].mode = mode[i];
p++;
}
}
count = p; if (p > 1)
MergeSort(order, count, sizeof(LockRequest), LessThanLockRequest);
result = TLS(lockStackPointer);
curr_prec = CurrentRegionPrecedence(); for (i = 0; i < count; i++) {
Region * region = order[i].region; /* If there are multiple lock requests with different modes, *theyhavebeensortedforwritestooccurfirst,sodeadlock *cannotoccurfromdoingreadlocksbeforewritelocks.
*/ if (i > 0 && region == order[i - 1].region) continue; // skip duplicates if (!region) continue;
LockStatus locked = IsLocked(region); if (locked == LOCK_STATUS_READONLY_LOCKED &&
order[i].mode == LOCK_MODE_READWRITE) { // trying to upgrade read lock to write lock
PopRegionLocks(result); return -1;
} if (!locked) { if (curr_prec >= 0 && region->prec >= curr_prec &&
region->prec >= 0) {
PopRegionLocks(result); return -1;
} if (region->fixed_owner) {
PopRegionLocks(result); return -1;
} if (order[i].mode == LOCK_MODE_READWRITE)
RegionWriteLock(region); else
RegionReadLock(region);
PushRegionLock(region);
} if (GetRegionOf(order[i].obj) != region) { // Race condition, revert locks and fail
PopRegionLocks(result); return -1;
}
} return result;
}
int TryLockObjects(int count, Obj * objects, const LockMode * mode)
{ int result; int i;
LockRequest * order; if (count > MAX_LOCKS) return -1;
order = alloca(sizeof(LockRequest) * count); for (i = 0; i < count; i++) {
order[i].obj = objects[i];
order[i].region = GetRegionOf(objects[i]);
order[i].mode = mode[i];
}
MergeSort(order, count, sizeof(LockRequest), LessThanLockRequest);
result = TLS(lockStackPointer); for (i = 0; i < count; i++) {
Region * region = order[i].region; /* If there are multiple lock requests with different modes, *theyhavebeensortedforwritestooccurfirst,sodeadlock *cannotoccurfromdoingreadlocksbeforewritelocks.
*/ if (i > 0 && region == order[i - 1].region) continue; // skip duplicates if (!region ||
region->fixed_owner) { // public or thread-local region
PopRegionLocks(result); return -1;
}
LockStatus locked = IsLocked(region); if (locked == LOCK_STATUS_READONLY_LOCKED &&
order[i].mode == LOCK_MODE_READWRITE) { // trying to upgrade read lock to write lock
PopRegionLocks(result); return -1;
} if (locked == LOCK_STATUS_UNLOCKED) { if (order[i].mode == LOCK_MODE_READWRITE) { if (!RegionTryWriteLock(region)) {
PopRegionLocks(result); return -1;
}
} else { if (!RegionTryReadLock(region)) {
PopRegionLocks(result); return -1;
}
}
PushRegionLock(region);
} if (GetRegionOf(order[i].obj) != region) { // Race condition, revert locks and fail
PopRegionLocks(result); return -1;
}
} return result;
}
Region * CurrentRegion(void)
{ return TLS(currentRegion);
}
#ifdef VERBOSE_GUARDS
staticvoid PrintGuardError(char * buffer, char * mode,
Obj obj, constchar * file, unsigned line, constchar * func, constchar * expr)
{
sprintf(buffer, "No %s access to object %llu of type %s\n" "in %s, line %u, function %s(), accessing %s",
mode, (unsignedlonglong)(UInt)obj, TNAM_OBJ(obj), file, line,
func, expr);
} void WriteGuardError(Obj o, constchar * file, unsigned line, constchar * func, constchar * expr)
{ char * buffer = alloca(strlen(file) + strlen(func) + strlen(expr) + 200);
ImpliedReadGuard(o); if (TLS(DisableGuards)) return;
SetGVar(&LastInaccessibleGVar, o);
PrintGuardError(buffer, "write", o, file, line, func, expr);
ErrorMayQuit("%s", (UInt)buffer, 0);
}
#else void WriteGuardError(Obj o)
{
ImpliedReadGuard(o); if (TLS(DisableGuards)) return;
SetGVar(&LastInaccessibleGVar, o);
ErrorMayQuit( "Attempt to write object %d of type %s without having write access",
(Int)o, (Int)TNAM_OBJ(o));
}
void ReadGuardError(Obj o)
{
ImpliedReadGuard(o); if (TLS(DisableGuards)) return;
SetGVar(&LastInaccessibleGVar, o);
ErrorMayQuit( "Attempt to read object %d of type %s without having read access",
(Int)o, (Int)TNAM_OBJ(o));
} #endif
#endif
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-10-12)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.