diff --git a/src/builtins.c b/src/builtins.c index 02d6791..b5e8fdb 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -393,6 +393,8 @@ KRK_METHOD(LicenseReader,__call__,{ _noexport void _createAndBind_builtins(void) { vm.baseClasses->objectClass = krk_newClass(S("object"), NULL); + krk_push(OBJECT_VAL(vm.baseClasses->objectClass)); + krk_defineNative(&vm.baseClasses->objectClass->methods, ":__class__", _type); krk_defineNative(&vm.baseClasses->objectClass->methods, ".__dir__", krk_dirObject); krk_defineNative(&vm.baseClasses->objectClass->methods, ".__str__", _strBase); @@ -401,6 +403,7 @@ void _createAndBind_builtins(void) { vm.baseClasses->objectClass->docstring = S("Base class for all types."); vm.baseClasses->moduleClass = krk_newClass(S("module"), vm.baseClasses->objectClass); + krk_push(OBJECT_VAL(vm.baseClasses->moduleClass)); krk_defineNative(&vm.baseClasses->moduleClass->methods, ".__repr__", _module_repr); krk_defineNative(&vm.baseClasses->moduleClass->methods, ".__str__", _module_repr); krk_finalizeClass(vm.baseClasses->moduleClass); @@ -409,6 +412,9 @@ void _createAndBind_builtins(void) { vm.builtins = krk_newInstance(vm.baseClasses->moduleClass); krk_attachNamedObject(&vm.modules, "__builtins__", (KrkObj*)vm.builtins); krk_attachNamedObject(&vm.builtins->fields, "object", (KrkObj*)vm.baseClasses->objectClass); + krk_pop(); + krk_pop(); + krk_attachNamedObject(&vm.builtins->fields, "__name__", (KrkObj*)S("__builtins__")); krk_attachNamedValue(&vm.builtins->fields, "__file__", NONE_VAL()); krk_attachNamedObject(&vm.builtins->fields, "__doc__", diff --git a/src/kuroko.c b/src/kuroko.c index 34512e8..b3bd609 100644 --- a/src/kuroko.c +++ b/src/kuroko.c @@ -148,10 +148,10 @@ static void tab_complete_func(rline_context_t * c) { int matchCount = 0; /* Take the last symbol name from the chain and get its member list from dir() */ - KRK_PAUSE_GC(); for (;;) { KrkValue dirList = krk_dirObject(1,(KrkValue[]){root}); + krk_push(dirList); if (!IS_INSTANCE(dirList)) { fprintf(stderr,"\nInternal error while tab completting.\n"); goto _cleanup; @@ -159,13 +159,17 @@ static void tab_complete_func(rline_context_t * c) { for (size_t i = 0; i < AS_LIST(dirList)->count; ++i) { KrkString * s = AS_STRING(AS_LIST(dirList)->values[i]); + krk_push(OBJECT_VAL(s)); KrkToken asToken = {.start = s->chars, .literalWidth = s->length}; KrkValue thisValue = findFromProperty(root, asToken); + krk_push(thisValue); if (IS_CLOSURE(thisValue) || IS_BOUND_METHOD(thisValue) || (IS_NATIVE(thisValue) && ((KrkNative*)AS_OBJECT(thisValue))->isMethod != 2)) { char * tmp = malloc(s->length + 2); sprintf(tmp, "%s(", s->chars); s = krk_takeString(tmp, strlen(tmp)); + krk_pop(); + krk_push(OBJECT_VAL(s)); } /* If this symbol is shorter than the current submatch, skip it. */ @@ -198,10 +202,11 @@ static void tab_complete_func(rline_context_t * c) { } else if (isGlobal && AS_OBJECT(root) == (KrkObj*)vm.builtins) { extern char * syn_krk_keywords[]; KrkInstance * fakeKeywordsObject = krk_newInstance(vm.baseClasses->objectClass); + root = OBJECT_VAL(fakeKeywordsObject); + krk_push(root); for (char ** keyword = syn_krk_keywords; *keyword; keyword++) { krk_attachNamedValue(&fakeKeywordsObject->fields, *keyword, NONE_VAL()); } - root = OBJECT_VAL(fakeKeywordsObject); continue; } else { break; @@ -262,7 +267,7 @@ _toomany: _cleanup: free(tmp); free(space); - KRK_RESUME_GC(); + krk_resetStack(); return; } } diff --git a/src/memory.c b/src/memory.c index 86561c1..2494dcb 100644 --- a/src/memory.c +++ b/src/memory.c @@ -207,12 +207,12 @@ static size_t sweep() { KrkObj * object = vm.objects; size_t count = 0; while (object) { - if (object->isMarked || object->isProtected) { + if (object->isMarked || object->isImmortal) { object->isMarked = 0; - object->isProtected = 0; + object->generation = 0; previous = object; object = object->next; - } else { + } else if (object->generation == 3) { KrkObj * unreached = object; object = object->next; if (previous != NULL) { @@ -222,6 +222,10 @@ static size_t sweep() { } freeObject(unreached); count++; + } else { + object->generation++; + previous = object; + object = object->next; } } return count; @@ -254,6 +258,10 @@ static void markThreadRoots(KrkThreadState * thread) { krk_markValue(thread->currentException); if (thread->module) krk_markObject((KrkObj*)thread->module); + + for (int i = 0; i < THREAD_SCRATCH_SIZE; ++i) { + krk_markValue(thread->scratchSpace[i]); + } } static void markRoots() { @@ -290,6 +298,34 @@ static KrkValue krk_collectGarbage_wrapper(int argc, KrkValue argv[]) { return INTEGER_VAL(krk_collectGarbage()); } +static KrkValue krk_generations(int argc, KrkValue argv[]) { +#define MAX_GEN 4 + krk_integer_type generations[MAX_GEN] = {0,0,0,0}; + KrkObj * object = vm.objects; + while (object) { + generations[object->generation]++; + object = object->next; + } + + /* Create a four-tuple */ + KrkTuple * outTuple = krk_newTuple(MAX_GEN); + for (int i = 0; i < MAX_GEN; ++i) { + outTuple->values.values[i] = INTEGER_VAL(generations[i]); + } + outTuple->values.count = MAX_GEN; + return OBJECT_VAL(outTuple); +} + +static KrkValue _gc_pause(int argc, KrkValue argv[]) { + vm.globalFlags |= (KRK_GC_PAUSED); + return NONE_VAL(); +} + +static KrkValue _gc_resume(int argc, KrkValue argv[]) { + vm.globalFlags &= ~(KRK_GC_PAUSED); + return NONE_VAL(); +} + _noexport void _createAndBind_gcMod(void) { /** @@ -302,6 +338,9 @@ void _createAndBind_gcMod(void) { krk_attachNamedObject(&gcModule->fields, "__name__", (KrkObj*)S("gc")); krk_attachNamedValue(&gcModule->fields, "__file__", NONE_VAL()); krk_defineNative(&gcModule->fields, "collect", krk_collectGarbage_wrapper); + krk_defineNative(&gcModule->fields, "generations", krk_generations); + krk_defineNative(&gcModule->fields, "pause", _gc_pause); + krk_defineNative(&gcModule->fields, "resume", _gc_resume); krk_attachNamedObject(&gcModule->fields, "__doc__", (KrkObj*)S("Namespace containing methods for controlling the garbge collector.")); } diff --git a/src/module_os.c b/src/module_os.c index 2662280..a343e52 100644 --- a/src/module_os.c +++ b/src/module_os.c @@ -23,27 +23,29 @@ extern char ** environ; KrkClass * OSError = NULL; +#define DO_KEY(key) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)krk_copyString(buf. key, strlen(buf .key))) +#define S_KEY(key,val) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)val); + #ifndef _WIN32 KRK_FUNC(uname,{ struct utsname buf; if (uname(&buf) < 0) return NONE_VAL(); - KRK_PAUSE_GC(); + KrkValue result = krk_dict_of(0, NULL); + krk_push(result); - KrkValue result = krk_dict_of(5 * 2, (KrkValue[]) { - OBJECT_VAL(S("sysname")), OBJECT_VAL(krk_copyString(buf.sysname,strlen(buf.sysname))), - OBJECT_VAL(S("nodename")), OBJECT_VAL(krk_copyString(buf.nodename,strlen(buf.nodename))), - OBJECT_VAL(S("release")), OBJECT_VAL(krk_copyString(buf.release,strlen(buf.release))), - OBJECT_VAL(S("version")), OBJECT_VAL(krk_copyString(buf.version,strlen(buf.version))), - OBJECT_VAL(S("machine")), OBJECT_VAL(krk_copyString(buf.machine,strlen(buf.machine))) - }); + DO_KEY(sysname); + DO_KEY(nodename); + DO_KEY(release); + DO_KEY(version); + DO_KEY(machine); - KRK_RESUME_GC(); - return result; + return krk_pop();; }) #else KRK_FUNC(uname,{ - KRK_PAUSE_GC(); + KrkValue result = krk_dict_of(0, NULL); + krk_push(result); TCHAR buffer[256] = TEXT(""); DWORD dwSize = sizeof(buffer); @@ -52,44 +54,37 @@ KRK_FUNC(uname,{ OSVERSIONINFOA versionInfo = {0}; versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionExA(&versionInfo); - KrkValue release; + if (versionInfo.dwMajorVersion == 10) { - release = OBJECT_VAL(S("10")); + S_KEY(release,S("10")); } else if (versionInfo.dwMajorVersion == 6) { if (versionInfo.dwMinorVersion == 3) { - release = OBJECT_VAL(S("8.1")); + S_KEY(release,S("8.1")); } else if (versionInfo.dwMinorVersion == 2) { - release = OBJECT_VAL(S("8.0")); + S_KEY(release,S("8.0")); } else if (versionInfo.dwMinorVersion == 1) { - release = OBJECT_VAL(S("7")); + S_KEY(release,S("7")); } else if (versionInfo.dwMinorVersion == 0) { - release = OBJECT_VAL(S("Vista")); + S_KEY(release,S("Vista")); } } else { - release = OBJECT_VAL(S("XP or earlier")); + S_KEY(release,S("XP or earlier")); } char tmp[256]; sprintf(tmp, "%ld", versionInfo.dwBuildNumber); - KrkValue version = OBJECT_VAL(krk_copyString(tmp,strlen(tmp))); - KrkValue machine; + S_KEY(version, krk_copyString(tmp,strlen(tmp))); if (sizeof(void *) == 8) { - machine = OBJECT_VAL(S("x64")); + S_KEY(machine,S("x64")); } else { - machine = OBJECT_VAL(S("x86")); + S_KEY(machine,S("x86")); } - KrkValue result = krk_dict_of(5 * 2, (KrkValue[]) { - OBJECT_VAL(S("sysname")), OBJECT_VAL(S("Windows")), - OBJECT_VAL(S("nodename")), OBJECT_VAL(krk_copyString(buffer,dwSize)), - OBJECT_VAL(S("release")), release, - OBJECT_VAL(S("version")), version, - OBJECT_VAL(S("machine")), machine - }); + S_KEY(sysname,S("Windows")); + S_KEY(nodename,krk_copyString(buffer,dwSize)); - KRK_RESUME_GC(); - return result; + return krk_pop(); }) #endif diff --git a/src/object.c b/src/object.c index e8a81eb..cd83b9d 100644 --- a/src/object.c +++ b/src/object.c @@ -16,6 +16,7 @@ static KrkObj * allocateObject(size_t size, ObjType type) { memset(object,0,size); object->type = type; object->next = vm.objects; + krk_currentThread.scratchSpace[2] = OBJECT_VAL(object); vm.objects = object; return object; } diff --git a/src/object.h b/src/object.h index 6f1739b..522dcf0 100644 --- a/src/object.h +++ b/src/object.h @@ -49,7 +49,8 @@ struct Obj { ObjType type; unsigned char isMarked:1; unsigned char inRepr:1; - unsigned char isProtected:1; + unsigned char generation:2; + unsigned char isImmortal:1; struct Obj * next; }; diff --git a/src/vm.c b/src/vm.c index c3d7df7..91d2bc4 100644 --- a/src/vm.c +++ b/src/vm.c @@ -623,19 +623,25 @@ static int call(KrkClosure * closure, int argCount, int extra) { size_t totalArguments = closure->function->requiredArgs + closure->function->keywordArgs + closure->function->collectsArguments + closure->function->collectsKeywords; size_t offsetOfExtraArgs = closure->function->requiredArgs + closure->function->keywordArgs; size_t argCountX = argCount; - KrkValueArray positionals; - KrkTable keywords; + KrkValueArray * positionals; + KrkTable * keywords; if (argCount && IS_KWARGS(krk_currentThread.stackTop[-1])) { - KRK_PAUSE_GC(); + + KrkValue myList = krk_list_of(0,NULL); + krk_currentThread.scratchSpace[0] = myList; + KrkValue myDict = krk_dict_of(0,NULL); + krk_currentThread.scratchSpace[1] = myDict; + positionals = AS_LIST(myList); + keywords = AS_DICT(myDict); /* This processes the existing argument list into a ValueArray and a Table with the args and keywords */ - if (!krk_processComplexArguments(argCount, &positionals, &keywords)) goto _errorDuringPositionals; + if (!krk_processComplexArguments(argCount, positionals, keywords)) goto _errorDuringPositionals; argCount--; /* It popped the KWARGS value from the top, so we have one less argument */ /* Do we already know we have too many arguments? Let's bail before doing a bunch of work. */ - if ((positionals.count > potentialPositionalArgs) && (!closure->function->collectsArguments)) { - checkArgumentCount(closure,positionals.count); + if ((positionals->count > potentialPositionalArgs) && (!closure->function->collectsArguments)) { + checkArgumentCount(closure,positionals->count); goto _errorDuringPositionals; } @@ -657,22 +663,23 @@ static int call(KrkClosure * closure, int argCount, int extra) { } /* Place positional arguments */ - for (size_t i = 0; i < potentialPositionalArgs && i < positionals.count; ++i) { - krk_currentThread.stackTop[-argCount + i] = positionals.values[i]; + for (size_t i = 0; i < potentialPositionalArgs && i < positionals->count; ++i) { + krk_currentThread.stackTop[-argCount + i] = positionals->values[i]; } if (closure->function->collectsArguments) { - size_t count = (positionals.count > potentialPositionalArgs) ? (positionals.count - potentialPositionalArgs) : 0; - KrkValue * offset = (count == 0) ? NULL : &positionals.values[potentialPositionalArgs]; + size_t count = (positionals->count > potentialPositionalArgs) ? (positionals->count - potentialPositionalArgs) : 0; + KrkValue * offset = (count == 0) ? NULL : &positionals->values[potentialPositionalArgs]; krk_push(krk_list_of(count, offset)); argCount++; } - krk_freeValueArray(&positionals); + krk_freeValueArray(positionals); + krk_currentThread.scratchSpace[0] = NONE_VAL(); /* Now place keyword arguments */ - for (size_t i = 0; i < keywords.capacity; ++i) { - KrkTableEntry * entry = &keywords.entries[i]; + for (size_t i = 0; i < keywords->capacity; ++i) { + KrkTableEntry * entry = &keywords->entries[i]; if (entry->key.type != VAL_KWARGS) { KrkValue name = entry->key; KrkValue value = entry->value; @@ -716,10 +723,11 @@ _finishKwarg: if (closure->function->collectsKeywords) { krk_push(krk_dict_of(0,NULL)); argCount++; - krk_tableAddAll(&keywords, AS_DICT(krk_peek(0))); + krk_tableAddAll(keywords, AS_DICT(krk_peek(0))); } - krk_freeTable(&keywords); + krk_freeTable(keywords); + krk_currentThread.scratchSpace[1] = NONE_VAL(); for (size_t i = 0; i < (size_t)closure->function->requiredArgs; ++i) { if (IS_KWARGS(krk_currentThread.stackTop[-argCount + i])) { @@ -730,7 +738,6 @@ _finishKwarg: } } - KRK_RESUME_GC(); argCountX = argCount - (closure->function->collectsArguments + closure->function->collectsKeywords); } else { /* We can't have had any kwargs. */ @@ -763,11 +770,12 @@ _finishKwarg: return 1; _errorDuringPositionals: - krk_freeValueArray(&positionals); + krk_freeValueArray(positionals); + krk_currentThread.scratchSpace[0] = NONE_VAL(); _errorAfterPositionals: - krk_freeTable(&keywords); + krk_freeTable(keywords); + krk_currentThread.scratchSpace[1] = NONE_VAL(); _errorAfterKeywords: - KRK_RESUME_GC(); return 0; } @@ -798,18 +806,19 @@ int krk_callValue(KrkValue callee, int argCount, int extra) { case OBJ_NATIVE: { NativeFnKw native = (NativeFnKw)AS_NATIVE(callee)->function; if (argCount && IS_KWARGS(krk_currentThread.stackTop[-1])) { - KRK_PAUSE_GC(); KrkValue myList = krk_list_of(0,NULL); + krk_currentThread.scratchSpace[0] = myList; KrkValue myDict = krk_dict_of(0,NULL); + krk_currentThread.scratchSpace[1] = myDict; if (!krk_processComplexArguments(argCount, AS_LIST(myList), AS_DICT(myDict))) { - KRK_RESUME_GC(); return 0; } - KRK_RESUME_GC(); argCount--; /* Because that popped the kwargs value */ krk_currentThread.stackTop -= argCount + extra; /* We can just put the stack back to normal */ krk_push(myList); krk_push(myDict); + krk_currentThread.scratchSpace[0] = NONE_VAL(); + krk_currentThread.scratchSpace[1] = NONE_VAL(); krk_writeValueArray(AS_LIST(myList), myDict); KrkValue result = native(AS_LIST(myList)->count-1, AS_LIST(myList)->values, 1); if (krk_currentThread.stackTop == krk_currentThread.stack) return 0; @@ -1073,7 +1082,6 @@ static KrkValue krk_setclean(int argc, KrkValue argv[]) { void krk_initVM(int flags) { vm.globalFlags = flags & 0xFF00; - KRK_PAUSE_GC(); /* Reset current thread */ krk_resetStack(); @@ -1208,7 +1216,6 @@ void krk_initVM(int flags) { /* The VM is now ready to start executing code. */ krk_resetStack(); - KRK_RESUME_GC(); } /** diff --git a/src/vm.h b/src/vm.h index 0ed2ce3..e68030a 100644 --- a/src/vm.h +++ b/src/vm.h @@ -118,6 +118,9 @@ typedef struct ThreadState { KrkValue currentException; int flags; long watchdog; + +#define THREAD_SCRATCH_SIZE 16 + KrkValue scratchSpace[THREAD_SCRATCH_SIZE]; } KrkThreadState; typedef struct { @@ -180,9 +183,6 @@ extern void krk_attachNamedValue(KrkTable * table, const char name[], KrkValue o extern KrkValue krk_runtimeError(KrkClass * type, const char * fmt, ...); extern KrkThreadState * krk_getCurrentThread(void); -#define KRK_PAUSE_GC() do { vm.globalFlags |= KRK_GC_PAUSED; } while (0) -#define KRK_RESUME_GC() do { vm.globalFlags &= ~(KRK_GC_PAUSED); } while (0) - extern KrkInstance * krk_dictCreate(void); extern KrkValue krk_runNext(void); extern KrkClass * krk_getType(KrkValue); diff --git a/test/testRacingThreads.krk b/test/testRacingThreads.krk new file mode 100644 index 0000000..ce88e7b --- /dev/null +++ b/test/testRacingThreads.krk @@ -0,0 +1,40 @@ +#### +# Demonstration of how container access is currently broken +# by threads and needs reader/writer locks. +#### +import os +if 'KUROKO_TEST_ENV' in os.environ: + return 0 + +from fileio import open, stdin +from threading import Thread + +let l = [] +let stop = False + +class Racer(Thread): + def run(self): + let myRando = open('/dev/urandom','rb') + while not stop: + let choice = myRando.read(1)[0] + if choice > 127: + l.append('test') + else if l: + l[choice % len(l)] += choice + +let racerA = Racer() +let racerB = Racer() + +racerA.start() +racerB.start() + +print("Press enter to stop.") +stdin.readline() + +stop = True + +print("Waiting for threads...") +racerA.join() +racerB.join() +print("Here's l:") +print(l) diff --git a/test/testRacingThreads.krk.expect b/test/testRacingThreads.krk.expect new file mode 100644 index 0000000..e69de29