/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file contains the GAP interface for thread primitives.
*/
/**************************************************************************** ** *F WaitForMonitor(monitor) . . . . . .. . wait for a monitor to be ready ** ** 'WaitForMonitor' waits for the monitor to be signaled by another ** thread. The monitor must be locked upon entry and will be locked ** again upon exit.
*/
staticint MonitorsAreSorted(UInt count, Monitor ** monitors)
{
UInt i; for (i = 1; i < count; i++) if ((char *)(monitors[i - 1]) > (char *)(monitors[i])) return 0; return 1;
}
void LockMonitors(UInt count, Monitor ** monitors)
{
UInt i;
assert(MonitorsAreSorted(count, monitors)); for (i = 0; i < count; i++)
LockMonitor(monitors[i]);
}
void UnlockMonitors(UInt count, Monitor ** monitors)
{
UInt i; for (i = 0; i < count; i++)
UnlockMonitor(monitors[i]);
}
/**************************************************************************** ** *F WaitForAnyMonitor(count, monitors) . . wait for a monitor to be ready ** ** 'WaitForAnyMonitor' waits for any one of the monitors in the list to ** be signaled. The function returns when any of them is signaled via ** 'SignalMonitor'. The first argument is the number of monitors in the ** list, the second argument is an array of monitor pointers. ** ** The list must be sorted by 'MonitorOrder' before passing it to the ** function; all monitors must also be locked before calling the function ** by calling 'LockMonitors'. ** ** Upon return, all monitors but the one that was signaled will be ** unlocked.
*/
UInt WaitForAnyMonitor(UInt count, Monitor ** monitors)
{ struct WaitList * nodes;
Monitor * monitor;
UInt i; Int result;
assert(MonitorsAreSorted(count, monitors));
nodes = alloca(sizeof(struct WaitList) * count); for (i = 0; i < count; i++)
nodes[i].thread = GetTLS(); for (i = 0; i < count; i++)
AddWaitList(monitors[i], &nodes[i]); for (i = 0; i < count; i++)
UnlockMonitor(monitors[i]);
LockThread(GetTLS()); while (!TLS(acquiredMonitor))
WaitThreadSignal();
monitor = TLS(acquiredMonitor);
UnlockThread(GetTLS());
// The following loops will initialize <result>, but the compiler // cannot know this; to avoid warnings, we set result to an // initial nonsense value.
result = -1; for (i = 0; i < count; i++) {
LockMonitor(monitors[i]); if (monitors[i] == monitor) {
RemoveWaitList(monitors[i], &nodes[i]);
result = i; // keep it locked for further processing by caller
} else {
RemoveWaitList(monitors[i], &nodes[i]);
UnlockMonitor(monitors[i]);
}
}
LockThread(GetTLS());
TLS(acquiredMonitor) = NULL;
UnlockThread(GetTLS()); return result;
}
/**************************************************************************** ** *F SignalMonitor(monitor) . . . . . . . . . . send a signal to a monitor ** ** Sends a signal to a monitor that is being waited for by another thread. ** The monitor must be locked upon entry and will be locked again upon ** exit. If no thread is waiting for the monitor, no operation will occur.
*/
staticint GetThreadID(constchar * funcname, Obj thread)
{ if (IS_INTOBJ(thread)) { Int id = INT_INTOBJ(thread); if (0 <= id && id < MAX_THREADS) return id;
} elseif (TNUM_OBJ(thread) == T_THREAD) { return ThreadID(thread);
}
RequireArgumentEx(funcname, thread, NICE_ARGNAME(thread), "must be a thread object or an integer between 0 and " "MAX_THREADS - 1");
}
staticvoid ThreadedInterpreter(void * funcargs)
{
Obj tmp, func; int i;
// initialize everything and begin a fresh execution context
tmp = KEPTALIVE(funcargs);
StopKeepAlive(funcargs);
func = ELM_PLIST(tmp, 1); for (i = 2; i <= LEN_PLIST(tmp); i++) {
Obj item = ELM_PLIST(tmp, i);
SET_ELM_PLIST(tmp, i - 1, item);
}
SET_LEN_PLIST(tmp, LEN_PLIST(tmp) - 1);
GAP_TRY
{
Obj init, exit; if (setjmp(TLS(threadExit))) return;
init = GVarOptFunction(&GVarTHREAD_INIT); if (init)
CALL_0ARGS(init);
CallFuncList(func, tmp); exit = GVarOptFunction(&GVarTHREAD_EXIT); if (exit)
CALL_0ARGS(exit);
}
GAP_CATCH
{
}
}
/**************************************************************************** ** *F FuncCreateThread ... create a new thread ** ** The function creates a new thread with a new interpreter and executes ** the function passed as an argument in it. It returns an integer that ** is a unique identifier for the thread.
*/
static Obj FuncCreateThread(Obj self, Obj funcargs)
{ Int i, n;
Obj thread;
Obj templist;
n = LEN_PLIST(funcargs); if (n == 0 || !IS_FUNC(ELM_PLIST(funcargs, 1))) return ArgumentError( "CreateThread: Needs at least one function argument");
Obj func = ELM_PLIST(funcargs, 1); if (NARG_FUNC(func) != n - 1)
ErrorMayQuit("CreateThread: expects %d arguments, but got %d", NARG_FUNC(func), n-1);
templist = NEW_PLIST(T_PLIST, n);
SET_LEN_PLIST(templist, n);
SET_REGION(templist, NULL); // make it public for (i = 1; i <= n; i++)
SET_ELM_PLIST(templist, i, ELM_PLIST(funcargs, i));
thread = RunThread(ThreadedInterpreter, KeepAlive(templist)); if (!thread) return Fail; return thread;
}
/**************************************************************************** ** *F FuncWaitThread ... wait for a created thread to finish. ** ** The function waits for an existing thread to finish.
*/
/**************************************************************************** ** *F FuncSetInterruptHandler ... set interrupt handler for current thread **
*/
static Obj FuncSetInterruptHandler(Obj self, Obj handler, Obj func)
{
RequireBoundedInt(SELF_NAME, handler, 0, MAX_INTERRUPT); if (func == Fail) {
SetInterruptHandler((int)(INT_INTOBJ(handler)), (Obj)0); return (Obj)0;
} if (TNUM_OBJ(func) != T_FUNCTION || NARG_FUNC(func) != 0 ||
!BODY_FUNC(func))
RequireArgument(SELF_NAME, func, "must be a parameterless function or 'fail'");
SetInterruptHandler((int)(INT_INTOBJ(handler)), func); return (Obj)0;
}
/**************************************************************************** ** *F FuncPauseThread ... pause a given thread **
*/
static Obj FuncPauseThread(Obj self, Obj thread)
{ int id = GetThreadID("PauseThread", thread);
PauseThread(id); return (Obj)0;
}
/**************************************************************************** ** *F FuncResumeThread ... resume a given thread **
*/
static Obj FuncResumeThread(Obj self, Obj thread)
{ int id = GetThreadID("ResumeThread", thread);
ResumeThread(id); return (Obj)0;
}
/**************************************************************************** ** *F FuncRegionOf ... return region of an object **
*/ static Obj PublicRegion;
static Obj FuncRegionOf(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj); return region == NULL ? PublicRegion : region->obj;
}
/**************************************************************************** ** *F FuncSetRegionName ... set the name of an object's region *F FuncClearRegionName ... clear the name of an object's region *F FuncRegionName ... get the name of an object's region **
*/
static Obj FuncSetRegionName(Obj self, Obj obj, Obj name)
{
Region * region = GetRegionOf(obj); if (!region)
ArgumentError( "SetRegionName: Cannot change name of the public region");
RequireStringRep(SELF_NAME, name);
SetRegionName(region, name); return (Obj)0;
}
static Obj FuncClearRegionName(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj); if (!region)
ArgumentError( "ClearRegionName: Cannot change name of the public region");
SetRegionName(region, (Obj)0); return (Obj)0;
}
static Obj FuncRegionName(Obj self, Obj obj)
{
Obj result;
Region * region = GetRegionOf(obj);
result = GetRegionName(region); if (!result)
result = Fail; return result;
}
/**************************************************************************** ** *F FuncIsShared ... return whether a region is shared **
*/
/**************************************************************************** ** *F FuncIsPublic ... return whether a region is public **
*/
static Obj FuncIsPublic(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj); return region == NULL ? True : False;
}
/**************************************************************************** ** *F FuncIsThreadLocal ... return whether a region is thread-local **
*/
/**************************************************************************** ** *F FuncHaveWriteAccess ... return if we have a write lock on the region **
*/
static Obj FuncHaveWriteAccess(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj); if (region != NULL &&
(region->owner == GetTLS() || region->alt_owner == GetTLS())) returnTrue; else returnFalse;
}
/**************************************************************************** ** *F FuncHaveReadAccess ... return if we have a read lock on the region **
*/
static Obj FuncHaveReadAccess(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj); if (region != NULL && CheckReadAccess(obj)) returnTrue; else returnFalse;
}
/**************************************************************************** ** *F FuncHASH_LOCK ........... acquire write lock on an object. *F FuncHASH_UNLOCK ......... release write lock on an object. *F FuncHASH_LOCK_SHARED ..... acquire read lock on an object. *F FuncHASH_UNLOCK_SHARED ... release read lock on an object. **
*/
/**************************************************************************** ** *F FuncHASH_SYNCHRONIZED ......... execute a function while holding a write *lock. *F FuncHASH_SYNCHRONIZED_SHARED ... execute a function while holding a read *lock. **
*/
staticvoid ExpandChannel(Channel * channel)
{ // Growth ratio should be less than the golden ratio const UInt oldCapacity = channel->capacity; const UInt newCapacity = ((oldCapacity * 25 / 16) | 1) + 1;
GAP_ASSERT(newCapacity > oldCapacity);
UInt i, tail;
Obj newqueue;
newqueue = NEW_PLIST(T_PLIST, newCapacity);
SET_LEN_PLIST(newqueue, newCapacity);
SET_REGION(newqueue, REGION(channel->queue));
channel->capacity = newCapacity; for (i = channel->head; i < oldCapacity; i++)
ADDR_OBJ(newqueue)[i + 1] = ADDR_OBJ(channel->queue)[i + 1]; for (i = 0; i < channel->tail; i++) {
UInt d = oldCapacity + i; if (d >= newCapacity)
d -= newCapacity;
ADDR_OBJ(newqueue)[d + 1] = ADDR_OBJ(channel->queue)[i + 1];
}
tail = channel->head + oldCapacity; if (tail >= newCapacity)
tail -= newCapacity;
channel->tail = tail;
channel->queue = newqueue;
}
staticvoid AddToChannel(Channel * channel, Obj obj, int migrate)
{
Obj children;
Region * region = REGION(channel->queue);
UInt i, len; if (migrate && IS_BAG_REF(obj) && REGION(obj) &&
REGION(obj)->owner == GetTLS() && REGION(obj)->fixed_owner) {
children = ReachableObjectsFrom(obj);
len = children ? LEN_PLIST(children) : 0;
} else {
children = 0;
len = 0;
} for (i = 1; i <= len; i++) {
Obj item = ELM_PLIST(children, i);
SET_REGION(item, region);
}
ADDR_OBJ(channel->queue)[++channel->tail] = obj;
ADDR_OBJ(channel->queue)[++channel->tail] = children; if (channel->tail == channel->capacity)
channel->tail = 0;
channel->size += 2;
}
static Obj RetrieveFromChannel(Channel * channel)
{
Obj obj = ADDR_OBJ(channel->queue)[++channel->head];
Obj children = ADDR_OBJ(channel->queue)[++channel->head];
Region * region = TLS(currentRegion);
UInt i, len = children ? LEN_PLIST(children) : 0;
ADDR_OBJ(channel->queue)[channel->head - 1] = 0;
ADDR_OBJ(channel->queue)[channel->head] = 0; if (channel->head == channel->capacity)
channel->head = 0; for (i = 1; i <= len; i++) {
Obj item = ELM_PLIST(children, i);
SET_REGION(item, region);
}
channel->size -= 2; return obj;
}
staticInt TallyChannel(Channel * channel)
{ Int result;
LockChannel(channel);
result = channel->size / 2;
UnlockChannel(channel); return result;
}
staticvoid SendChannel(Channel * channel, Obj obj, int migrate)
{
LockChannel(channel); if (channel->size == channel->capacity && channel->dynamic)
ExpandChannel(channel); while (channel->size == channel->capacity)
WaitChannel(channel);
AddToChannel(channel, obj, migrate);
SignalChannel(channel);
UnlockChannel(channel);
}
staticvoid MultiSendChannel(Channel * channel, Obj list, int migrate)
{ int listsize = LEN_LIST(list); int i;
Obj obj;
LockChannel(channel); for (i = 1; i <= listsize; i++) { if (channel->size == channel->capacity && channel->dynamic)
ExpandChannel(channel); while (channel->size == channel->capacity)
WaitChannel(channel);
obj = ELM_LIST(list, i);
AddToChannel(channel, obj, migrate);
}
SignalChannel(channel);
UnlockChannel(channel);
}
staticint TryMultiSendChannel(Channel * channel, Obj list, int migrate)
{ int result = 0; int listsize = LEN_LIST(list); int i;
Obj obj;
LockChannel(channel); for (i = 1; i <= listsize; i++) { if (channel->size == channel->capacity && channel->dynamic)
ExpandChannel(channel); if (channel->size == channel->capacity) break;
obj = ELM_LIST(list, i);
AddToChannel(channel, obj, migrate);
result++;
}
SignalChannel(channel);
UnlockChannel(channel); return result;
}
static Obj ReceiveAnyChannel(Obj channelList, int with_index)
{
UInt count = LEN_PLIST(channelList);
UInt i, p;
Monitor ** monitors = alloca(count * sizeof(Monitor *));
Channel ** channels = alloca(count * sizeof(Channel *));
Obj result;
Channel * channel; for (i = 0; i < count; i++)
channels[i] = ObjPtr(ELM_PLIST(channelList, i + 1));
SortChannels(count, channels); for (i = 0; i < count; i++)
monitors[i] = ObjPtr(channels[i]->monitor);
LockMonitors(count, monitors);
p = TLS(multiplexRandomSeed);
p = (p * 5 + 1);
TLS(multiplexRandomSeed) = p;
p %= count; for (i = 0; i < count; i++) {
channel = channels[p]; if (channel->size > 0) break;
p++; if (p >= count)
p = 0;
} if (i < count) // found a channel with data
{
p = i; for (i = 0; i < count; i++) if (i != p)
UnlockMonitor(monitors[i]);
} else// all channels are empty for (;;) { for (i = 0; i < count; i++)
channels[i]->waiting++;
p = WaitForAnyMonitor(count, monitors); for (i = 0; i < count; i++)
channels[i]->waiting--;
channel = channels[p]; if (channel->size > 0) break;
UnlockMonitor(monitors[p]);
LockMonitors(count, monitors);
}
result = RetrieveFromChannel(channel);
SignalChannel(channel);
UnlockMonitor(monitors[p]); if (with_index) {
Obj list = NEW_PLIST(T_PLIST, 2);
SET_LEN_PLIST(list, 2);
SET_ELM_PLIST(list, 1, result); for (i = 1; i <= count; i++) if (ObjPtr(ELM_PLIST(channelList, i)) == channel) {
SET_ELM_PLIST(list, 2, INTOBJ_INT(i)); break;
} return list;
} else return result;
}
static Obj MultiReceiveChannel(Channel * channel, UInt max)
{
Obj result;
UInt count;
UInt i;
LockChannel(channel); if (max > channel->size / 2)
count = channel->size / 2; else
count = max;
result = NEW_PLIST(T_PLIST, count);
SET_LEN_PLIST(result, count); for (i = 0; i < count; i++) {
Obj item = RetrieveFromChannel(channel);
SET_ELM_PLIST(result, i + 1, item);
}
SignalChannel(channel);
UnlockChannel(channel); return result;
}
static Obj InspectChannel(Channel * channel)
{
LockChannel(channel); const UInt count = channel->size / 2;
Obj result = NEW_PLIST(T_PLIST, count);
SET_LEN_PLIST(result, count); for (UInt i = 0, p = channel->head; i < count; i++) {
SET_ELM_PLIST(result, i + 1, ELM_PLIST(channel->queue, p + 1));
p += 2; if (p == channel->capacity)
p = 0;
}
UnlockChannel(channel); return result;
}
static Obj FuncCreateChannel(Obj self, Obj args)
{ int capacity; switch (LEN_PLIST(args)) { case 0:
capacity = -1; break; case 1: if (IS_INTOBJ(ELM_PLIST(args, 1))) {
capacity = INT_INTOBJ(ELM_PLIST(args, 1)); if (capacity <= 0) return ArgumentError( "CreateChannel: Capacity must be positive"); break;
} return ArgumentError( "CreateChannel: Argument must be capacity of the channel"); default: return ArgumentError( "CreateChannel: Function takes up to one argument");
} return CreateChannel(capacity);
}
staticBOOL IsChannelList(Obj list)
{ int len = LEN_PLIST(list); int i; if (len == 0) return 0; for (i = 1; i <= len; i++) if (!IsChannel(ELM_PLIST(list, i))) return 0; return 1;
}
static Obj FuncReceiveAnyChannel(Obj self, Obj args)
{ if (IsChannelList(args)) return ReceiveAnyChannel(args, 0); else { if (LEN_PLIST(args) == 1 && IS_PLIST(ELM_PLIST(args, 1)) &&
IsChannelList(ELM_PLIST(args, 1))) return ReceiveAnyChannel(ELM_PLIST(args, 1), 0); else return ArgumentError( "ReceiveAnyChannel: Argument list must be channels");
}
}
static Obj FuncReceiveAnyChannelWithIndex(Obj self, Obj args)
{ if (IsChannelList(args)) return ReceiveAnyChannel(args, 1); else { if (LEN_PLIST(args) == 1 && IS_PLIST(ELM_PLIST(args, 1)) &&
IsChannelList(ELM_PLIST(args, 1))) return ReceiveAnyChannel(ELM_PLIST(args, 1), 1); else return ArgumentError( "ReceiveAnyChannelWithIndex: Argument list must be channels");
}
}
static Obj FuncCreateSemaphore(Obj self, Obj args)
{ Int count; switch (LEN_PLIST(args)) { case 0:
count = 0; break; case 1: if (IS_INTOBJ(ELM_PLIST(args, 1))) {
count = INT_INTOBJ(ELM_PLIST(args, 1)); if (count < 0) return ArgumentError( "CreateSemaphore: Initial count must be non-negative"); break;
} return ArgumentError( "CreateSemaphore: Argument must be initial count"); default: return ArgumentError( "CreateSemaphore: Function takes up to two arguments");
} return CreateSemaphore(count);
}
static Obj FuncCURRENT_LOCKS(Obj self)
{
UInt i, len = TLS(lockStackPointer);
Obj result = NEW_PLIST(T_PLIST, len);
SET_LEN_PLIST(result, len); for (i = 1; i <= len; i++)
SET_ELM_PLIST(result, i, ELM_PLIST(TLS(lockStack), i)); return result;
}
static Obj FuncNEW_REGION(Obj self, Obj name, Obj prec)
{ if (name != Fail && !IsStringConv(name))
RequireArgument(SELF_NAME, name, "must be a string or fail"); Int p = GetSmallInt(SELF_NAME, prec);
Region * region = NewRegion();
region->prec = p; if (name != Fail)
SetRegionName(region, name); return region->obj;
}
static Obj FuncREGION_PRECEDENCE(Obj self, Obj regobj)
{
Region * region = GetRegionOf(regobj); return region == NULL ? INTOBJ_INT(0) : INTOBJ_INT(region->prec);
}
staticint AutoRetyping = 0;
staticint
MigrateObjects(int count, Obj * objects, Region * target, int retype)
{ int i; if (count && retype && IS_BAG_REF(objects[0]) &&
REGION(objects[0])->owner == GetTLS() && AutoRetyping) { for (i = 0; i < count; i++) if (REGION(objects[i])->owner == GetTLS())
CLEAR_OBJ_FLAG(objects[i], OBJ_FLAG_TESTED); for (i = 0; i < count; i++) { if (REGION(objects[i])->owner == GetTLS() &&
IS_PLIST(objects[i])) { if (!TEST_OBJ_FLAG(objects[i], OBJ_FLAG_TESTED))
TYPE_OBJ(objects[i]); if (retype >= 2)
IS_SSORT_LIST(objects[i]); // record if the list a set in the tnum
}
}
} for (i = 0; i < count; i++) {
Region * region; if (IS_BAG_REF(objects[i])) {
region = REGION(objects[i]); if (!region || region->owner != GetTLS()) return 0;
}
} // If we are migrating records to a region where they become immutable, // they need to be sorted, as sorting upon access may prove impossible. for (i = 0; i < count; i++) {
Obj obj = objects[i]; if (TNUM_OBJ(obj) == T_PREC) {
SortPRecRNam(obj);
}
SET_REGION(obj, target);
} return 1;
}
static Obj FuncSHARE(Obj self, Obj obj, Obj name, Obj prec)
{ if (name != Fail && !IsStringConv(name))
RequireArgument(SELF_NAME, name, "must be a string or fail"); Int p = GetSmallInt(SELF_NAME, prec);
Region * region = NewRegion();
region->prec = p;
Obj reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1, region,
1)) return ArgumentError( "SHARE: Thread does not have exclusive access to objects"); if (name != Fail)
SetRegionName(region, name); return obj;
}
static Obj FuncSHARE_RAW(Obj self, Obj obj, Obj name, Obj prec)
{ if (name != Fail && !IsStringConv(name))
RequireArgument(SELF_NAME, name, "must be a string or fail"); Int p = GetSmallInt(SELF_NAME, prec);
Region * region = NewRegion();
region->prec = p;
Obj reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1, region,
0)) return ArgumentError( "SHARE_RAW: Thread does not have exclusive access to objects"); if (name != Fail)
SetRegionName(region, name); return obj;
}
static Obj FuncSHARE_NORECURSE(Obj self, Obj obj, Obj name, Obj prec)
{ if (name != Fail && !IsStringConv(name))
RequireArgument(SELF_NAME, name, "must be a string or fail"); Int p = GetSmallInt(SELF_NAME, prec);
Region * region = NewRegion();
region->prec = p; if (!MigrateObjects(1, &obj, region, 0)) return ArgumentError("SHARE_NORECURSE: Thread does not have " "exclusive access to objects"); if (name != Fail)
SetRegionName(region, name); return obj;
}
static Obj FuncADOPT(Obj self, Obj obj)
{
Obj reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1,
TLS(threadRegion), 0)) return ArgumentError( "ADOPT: Thread does not have exclusive access to objects"); return obj;
}
static Obj FuncADOPT_NORECURSE(Obj self, Obj obj)
{ if (!MigrateObjects(1, &obj, TLS(threadRegion), 0)) return ArgumentError("ADOPT_NORECURSE: Thread does not have " "exclusive access to objects"); return obj;
}
static Obj FuncMIGRATE(Obj self, Obj obj, Obj target)
{
Region * target_region = GetRegionOf(target);
Obj reachable; if (!target_region ||
IsLocked(target_region) != LOCK_STATUS_READWRITE_LOCKED) return ArgumentError("MIGRATE: Thread does not have exclusive access " "to target region");
reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1,
target_region, 1)) return ArgumentError( "MIGRATE: Thread does not have exclusive access to objects"); return obj;
}
static Obj FuncMIGRATE_RAW(Obj self, Obj obj, Obj target)
{
Region * target_region = GetRegionOf(target);
Obj reachable; if (!target_region ||
IsLocked(target_region) != LOCK_STATUS_READWRITE_LOCKED) return ArgumentError("MIGRATE_RAW: Thread does not have exclusive access " "to target region");
reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1,
target_region, 0)) return ArgumentError( "MIGRATE_RAW: Thread does not have exclusive access to objects"); return obj;
}
static Obj FuncMIGRATE_NORECURSE(Obj self, Obj obj, Obj target)
{
Region * target_region = GetRegionOf(target); if (!target_region ||
IsLocked(target_region) != LOCK_STATUS_READWRITE_LOCKED) return ArgumentError("MIGRATE_NORECURSE: Thread does not have " "exclusive access to target region"); if (!MigrateObjects(1, &obj, target_region, 0)) return ArgumentError("MIGRATE_NORECURSE: Thread does not have " "exclusive access to object"); return obj;
}
static Obj FuncMAKE_PUBLIC(Obj self, Obj obj)
{
Obj reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1, NULL,
0)) return ArgumentError( "MAKE_PUBLIC: Thread does not have exclusive access to objects"); return obj;
}
static Obj FuncMAKE_PUBLIC_NORECURSE(Obj self, Obj obj)
{ if (!MigrateObjects(1, &obj, NULL, 0)) return ArgumentError("MAKE_PUBLIC_NORECURSE: Thread does not have " "exclusive access to objects"); return obj;
}
static Obj FuncFORCE_MAKE_PUBLIC(Obj self, Obj obj)
{ if (!IS_BAG_REF(obj)) return ArgumentError("FORCE_MAKE_PUBLIC: Argument is a small integer " "or finite-field element");
MakeBagPublic(obj); return obj;
}
static Obj FuncMakeThreadLocal(Obj self, Obj var)
{ constchar * name;
UInt gvar; if (!IsStringConv(var) || GET_LEN_STRING(var) == 0)
RequireArgument(SELF_NAME, var, "must be a variable name");
name = CONST_CSTR_STRING(var);
gvar = GVarName(name);
name = CONST_CSTR_STRING(NameGVar(gvar)); // to apply namespace scopes where needed
MakeThreadLocalVar(gvar, RNamName(name)); return (Obj)0;
}
static Obj FuncMakeReadOnlyObj(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj);
Obj reachable; if (!region || region == ReadOnlyRegion) return obj;
reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1,
ReadOnlyRegion, 1)) return ArgumentError( "MakeReadOnlyObj: Thread does not have exclusive access to objects"); return obj;
}
static Obj FuncMakeReadOnlyRaw(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj);
Obj reachable; if (!region || region == ReadOnlyRegion) return obj;
reachable = ReachableObjectsFrom(obj); if (!MigrateObjects(LEN_PLIST(reachable), ADDR_OBJ(reachable) + 1,
ReadOnlyRegion, 0)) return ArgumentError( "MakeReadOnlyObj: Thread does not have exclusive access to objects"); return obj;
}
static Obj FuncMakeReadOnlySingleObj(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj); if (!region || region == ReadOnlyRegion) return obj; if (!MigrateObjects(1, &obj, ReadOnlyRegion, 0)) return ArgumentError("MakeReadOnlySingleObj: Thread does not have " "exclusive access to object"); return obj;
}
static Obj FuncSIGWAIT(Obj self, Obj handlers)
{ int sig; if (!IS_REC(handlers)) return ArgumentError("SIGWAIT: Argument must be a record"); if (sigwait(&GAPSignals, &sig) >= 0) { switch (sig) { case SIGINT:
HandleSignal(handlers, RNAM_SIGINT); break; case SIGCHLD:
HandleSignal(handlers, RNAM_SIGCHLD); break; case SIGVTALRM:
HandleSignal(handlers, RNAM_SIGVTALRM); break; #ifdef SIGWINCH case SIGWINCH:
HandleSignal(handlers, RNAM_SIGWINCH); break; #endif
}
} return (Obj)0;
}
void InitSignals(void)
{ struct itimerval timer;
sigemptyset(&GAPSignals);
sigaddset(&GAPSignals, SIGINT);
sigaddset(&GAPSignals, SIGCHLD);
sigaddset(&GAPSignals, SIGVTALRM); #ifdef SIGWINCH
sigaddset(&GAPSignals, SIGWINCH); #endif
pthread_sigmask(SIG_BLOCK, &GAPSignals, NULL); // Run a timer signal every 10 ms, i.e. 100 times per second
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 10000;
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 10000;
setitimer(ITIMER_VIRTUAL, &timer, NULL);
}
static Obj FuncPERIODIC_CHECK(Obj self, Obj count, Obj func)
{
UInt n;
RequireNonnegativeSmallInt(SELF_NAME, count);
RequireFunction(SELF_NAME, func); /* * The following read of SigVTALRMCounter is a dirty read. We don't * need to synchronize access to it because it's a monotonically * increasing value and we only need it to succeed eventually.
*/
n = INT_INTOBJ(count) / 10; if (TLS(PeriodicCheckCount) + n < SigVTALRMCounter) {
TLS(PeriodicCheckCount) = SigVTALRMCounter;
CALL_0ARGS(func);
} return (Obj)0;
}
/* * Region lock performance counters
*/ static Obj FuncREGION_COUNTERS_ENABLE(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj);
if (!region) return ArgumentError( "REGION_COUNTERS_ENABLE: Cannot enable counters for this region");
region->count_active = 1; return (Obj)0;
}
static Obj FuncREGION_COUNTERS_DISABLE(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj);
if (!region) return ArgumentError("REGION_COUNTERS_DISABLE: Cannot disable " "counters for this region");
region->count_active = 0; return (Obj)0;
}
static Obj FuncREGION_COUNTERS_GET_STATE(Obj self, Obj obj)
{
Obj result;
Region * region = GetRegionOf(obj);
if (!region) return ArgumentError( "REGION_COUNTERS_GET_STATE: Cannot get counters for this region");
result = INTOBJ_INT(region->count_active);
return result;
}
static Obj FuncREGION_COUNTERS_GET(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj);
if (!region) return ArgumentError( "REGION_COUNTERS_GET: Cannot get counters for this region");
return GetRegionLockCounters(region);
}
static Obj FuncREGION_COUNTERS_RESET(Obj self, Obj obj)
{
Region * region = GetRegionOf(obj);
if (!region) return ArgumentError( "REGION_COUNTERS_RESET: Cannot reset counters for this region");
¤ 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.0.49Bemerkung:
(vorverarbeitet)
¤
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 ist noch experimentell.