mirror of https://gitlab.com/nakst/essence
scripting engine SystemShellExecute(WithWorkingDirectory) async
This commit is contained in:
parent
a0a1365463
commit
63521a9f62
161
util/script.c
161
util/script.c
|
@ -1,4 +1,5 @@
|
||||||
// TODO StringJoin is extremely slow and uses a lot of memory. Add a StringBuilder.
|
// TODO StringJoin is extremely slow and uses a lot of memory.
|
||||||
|
// Add a StringBuilder? Or maybe a T_CONCAT heap object?
|
||||||
|
|
||||||
// TODO Basic missing features:
|
// TODO Basic missing features:
|
||||||
// - Other list operations: insert_many, delete, delete_many, delete_last.
|
// - Other list operations: insert_many, delete, delete_many, delete_last.
|
||||||
|
@ -285,9 +286,11 @@ typedef struct CoroutineState {
|
||||||
size_t waitersAllocated;
|
size_t waitersAllocated;
|
||||||
struct CoroutineState ***waitingOn;
|
struct CoroutineState ***waitingOn;
|
||||||
size_t waitingOnCount;
|
size_t waitingOnCount;
|
||||||
bool awaiting, startedByAsync;
|
bool awaiting, startedByAsync, externalCoroutine;
|
||||||
uintptr_t instructionPointer;
|
uintptr_t instructionPointer;
|
||||||
uintptr_t variableBase;
|
uintptr_t variableBase;
|
||||||
|
Value externalCoroutineData;
|
||||||
|
void *externalCoroutineData2;
|
||||||
} CoroutineState;
|
} CoroutineState;
|
||||||
|
|
||||||
typedef struct ExecutionContext {
|
typedef struct ExecutionContext {
|
||||||
|
@ -308,6 +311,7 @@ typedef struct ExecutionContext {
|
||||||
CoroutineState *allCoroutines;
|
CoroutineState *allCoroutines;
|
||||||
CoroutineState *unblockedCoroutines;
|
CoroutineState *unblockedCoroutines;
|
||||||
uint64_t lastCoroutineID;
|
uint64_t lastCoroutineID;
|
||||||
|
uint32_t externalCoroutineCount;
|
||||||
} ExecutionContext;
|
} ExecutionContext;
|
||||||
|
|
||||||
typedef struct ExternalFunction {
|
typedef struct ExternalFunction {
|
||||||
|
@ -367,6 +371,7 @@ void PrintError3(const char *format, ...);
|
||||||
void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const char *format, ...);
|
void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const char *format, ...);
|
||||||
void PrintBackTrace(ExecutionContext *context, uint32_t instructionPointer, CoroutineState *c, const char *prefix);
|
void PrintBackTrace(ExecutionContext *context, uint32_t instructionPointer, CoroutineState *c, const char *prefix);
|
||||||
void *FileLoad(const char *path, size_t *length);
|
void *FileLoad(const char *path, size_t *length);
|
||||||
|
CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context);
|
||||||
|
|
||||||
// --------------------------------- Base module.
|
// --------------------------------- Base module.
|
||||||
|
|
||||||
|
@ -452,8 +457,6 @@ char baseModuleSource[] = {
|
||||||
|
|
||||||
// --------------------------------- External function calls.
|
// --------------------------------- External function calls.
|
||||||
|
|
||||||
int ExternalPrintInt(ExecutionContext *context, Value *returnValue);
|
|
||||||
int ExternalPrintString(ExecutionContext *context, Value *returnValue);
|
|
||||||
int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue);
|
int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue);
|
||||||
int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue);
|
int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue);
|
||||||
int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue);
|
int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue);
|
||||||
|
@ -2245,7 +2248,7 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
|
||||||
if (!expressionType->firstChild->firstChild) {
|
if (!expressionType->firstChild->firstChild) {
|
||||||
PrintError2(tokenizer, node, "The function pointer doesn't take any arguments.\n");
|
PrintError2(tokenizer, node, "The function pointer doesn't take any arguments.\n");
|
||||||
return false;
|
return false;
|
||||||
} else if (!ASTMatching(expressionType->firstChild->firstChild->expressionType, node->firstChild->sibling->firstChild->expressionType)) {
|
} else if (!ASTMatching(expressionType->firstChild->firstChild->firstChild, node->firstChild->sibling->firstChild->expressionType)) {
|
||||||
PrintError2(tokenizer, node, "The curried argument does not match the type of the first argument.\n");
|
PrintError2(tokenizer, node, "The curried argument does not match the type of the first argument.\n");
|
||||||
return false;
|
return false;
|
||||||
} else if (node->firstChild->sibling->firstChild->sibling) {
|
} else if (node->firstChild->sibling->firstChild->sibling) {
|
||||||
|
@ -2930,9 +2933,6 @@ bool ASTGenerate(Tokenizer *tokenizer, Node *root, ExecutionContext *context) {
|
||||||
context->globalVariableIsManaged[context->functionData->globalVariableOffset + i] = false;
|
context->globalVariableIsManaged[context->functionData->globalVariableOffset + i] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t zero = 0; // Make sure no function can start at 0.
|
|
||||||
FunctionBuilderAppend(context->functionData, &zero, sizeof(zero));
|
|
||||||
|
|
||||||
while (child) {
|
while (child) {
|
||||||
if (child->type == T_FUNCTION) {
|
if (child->type == T_FUNCTION) {
|
||||||
uintptr_t variableIndex = context->functionData->globalVariableOffset + ScopeLookupIndex(child, root->scope, false, false);
|
uintptr_t variableIndex = context->functionData->globalVariableOffset + ScopeLookupIndex(child, root->scope, false, false);
|
||||||
|
@ -3137,7 +3137,7 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
uint8_t command = functionData[instructionPointer++];
|
uint8_t command = functionData[instructionPointer++];
|
||||||
// PrintDebug("--> %d, %ld, %d\n", command, context->c->id);
|
// PrintDebug("--> %d, %ld, %ld\n", command, instructionPointer - 1, context->c->id);
|
||||||
|
|
||||||
if (command == T_BLOCK || command == T_FUNCBODY) {
|
if (command == T_BLOCK || command == T_FUNCBODY) {
|
||||||
uint16_t newVariableCount = functionData[instructionPointer + 0] + (functionData[instructionPointer + 1] << 8);
|
uint16_t newVariableCount = functionData[instructionPointer + 0] + (functionData[instructionPointer + 1] << 8);
|
||||||
|
@ -4013,12 +4013,21 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
|
||||||
context->c->stackIsManaged[context->c->stackPointer - 1] = false;
|
context->c->stackIsManaged[context->c->stackPointer - 1] = false;
|
||||||
context->c->stack[context->c->stackPointer - 1].i = c->id;
|
context->c->stack[context->c->stackPointer - 1].i = c->id;
|
||||||
} else if (command == T_AWAIT) {
|
} else if (command == T_AWAIT) {
|
||||||
if (context->c->stackPointer < 1) return -1;
|
awaitCommand:;
|
||||||
|
if (context->c->stackPointer < 1 && !context->c->externalCoroutine) return -1;
|
||||||
|
|
||||||
// PrintDebug("== AWAIT from %ld\n", context->c->id);
|
// PrintDebug("== AWAIT from %ld\n", context->c->id);
|
||||||
Assert(!context->c->nextUnblockedCoroutine && !context->c->previousUnblockedCoroutineLink);
|
Assert(!context->c->nextUnblockedCoroutine && !context->c->previousUnblockedCoroutineLink);
|
||||||
|
bool unblockImmediately = false;
|
||||||
|
|
||||||
if (context->c->stack[context->c->stackPointer - 1].i == -1) {
|
if (context->c->externalCoroutine) {
|
||||||
|
// PrintDebug("== external coroutine\n");
|
||||||
|
context->c->unblockedBy = -1;
|
||||||
|
context->c->awaiting = true;
|
||||||
|
context->c->instructionPointer = instructionPointer;
|
||||||
|
context->c->variableBase = variableBase;
|
||||||
|
context->c->waitingOnCount = 0;
|
||||||
|
} else if (context->c->stack[context->c->stackPointer - 1].i == -1) {
|
||||||
if (context->c->stackPointer != 1) return -1;
|
if (context->c->stackPointer != 1) return -1;
|
||||||
// The coroutine has finished. Remove it from the list of all coroutines.
|
// The coroutine has finished. Remove it from the list of all coroutines.
|
||||||
*context->c->previousCoroutineLink = context->c->nextCoroutine;
|
*context->c->previousCoroutineLink = context->c->nextCoroutine;
|
||||||
|
@ -4084,12 +4093,20 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
|
||||||
if (!context->c->waitingOnCount) {
|
if (!context->c->waitingOnCount) {
|
||||||
// PrintDebug("== immediately unblocking\n");
|
// PrintDebug("== immediately unblocking\n");
|
||||||
context->c->unblockedBy = entry->length ? entry->list[0].i : -1;
|
context->c->unblockedBy = entry->length ? entry->list[0].i : -1;
|
||||||
|
unblockImmediately = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineState *next = context->unblockedCoroutines;
|
CoroutineState *next = unblockImmediately ? context->c : context->unblockedCoroutines;
|
||||||
|
|
||||||
if (!next) {
|
if (!next) {
|
||||||
|
if (context->externalCoroutineCount) {
|
||||||
|
// PrintDebug("== wait for an external coroutine\n");
|
||||||
|
next = ExternalCoroutineWaitAny(context);
|
||||||
|
Assert(next->externalCoroutine);
|
||||||
|
unblockImmediately = true;
|
||||||
|
} else {
|
||||||
|
// TODO Earlier deadlock detection.
|
||||||
PrintError4(context, instructionPointer - 1, "No tasks can run if this task (ID %ld) starts waiting.\n", context->c->id);
|
PrintError4(context, instructionPointer - 1, "No tasks can run if this task (ID %ld) starts waiting.\n", context->c->id);
|
||||||
PrintDebug("All tasks:\n");
|
PrintDebug("All tasks:\n");
|
||||||
CoroutineState *c = context->allCoroutines;
|
CoroutineState *c = context->allCoroutines;
|
||||||
|
@ -4111,24 +4128,31 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unblockImmediately) {
|
||||||
Assert(next->previousUnblockedCoroutineLink);
|
Assert(next->previousUnblockedCoroutineLink);
|
||||||
*next->previousUnblockedCoroutineLink = next->nextUnblockedCoroutine;
|
*next->previousUnblockedCoroutineLink = next->nextUnblockedCoroutine;
|
||||||
if (next->nextUnblockedCoroutine) next->nextUnblockedCoroutine->previousUnblockedCoroutineLink = next->previousUnblockedCoroutineLink;
|
if (next->nextUnblockedCoroutine) next->nextUnblockedCoroutine->previousUnblockedCoroutineLink = next->previousUnblockedCoroutineLink;
|
||||||
|
}
|
||||||
|
|
||||||
next->nextUnblockedCoroutine = NULL;
|
next->nextUnblockedCoroutine = NULL;
|
||||||
next->previousUnblockedCoroutineLink = NULL;
|
next->previousUnblockedCoroutineLink = NULL;
|
||||||
context->c = next;
|
context->c = next;
|
||||||
// PrintDebug("== switch to %ld\n", next->id);
|
// PrintDebug("== switch to %ld\n", next->id);
|
||||||
|
|
||||||
if (context->c->awaiting) {
|
if (context->c->awaiting) {
|
||||||
|
if (!context->c->externalCoroutine) {
|
||||||
context->c->stackIsManaged[context->c->stackPointer - 1] = false;
|
context->c->stackIsManaged[context->c->stackPointer - 1] = false;
|
||||||
context->c->stack[context->c->stackPointer - 1].i = context->c->unblockedBy;
|
context->c->stack[context->c->stackPointer - 1].i = context->c->unblockedBy;
|
||||||
|
}
|
||||||
|
|
||||||
instructionPointer = context->c->instructionPointer;
|
instructionPointer = context->c->instructionPointer;
|
||||||
variableBase = context->c->variableBase;
|
variableBase = context->c->variableBase;
|
||||||
// PrintDebug("== unblocked by %ld\n", context->c->unblockedBy);
|
// PrintDebug("== unblocked by %ld\n", context->c->unblockedBy);
|
||||||
} else {
|
} else {
|
||||||
// PrintDebug("== just started\n");
|
// PrintDebug("== just started\n");
|
||||||
instructionPointer--;
|
instructionPointer = 1; // There is a T_AWAIT command at address 1.
|
||||||
goto callCommand;
|
goto callCommand;
|
||||||
}
|
}
|
||||||
} else if (command == T_END_FUNCTION || command == T_EXTCALL) {
|
} else if (command == T_END_FUNCTION || command == T_EXTCALL) {
|
||||||
|
@ -4141,6 +4165,18 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
|
||||||
int result = externalFunctions[index].callback(context, &returnValue);
|
int result = externalFunctions[index].callback(context, &returnValue);
|
||||||
if (result <= 0) return result;
|
if (result <= 0) return result;
|
||||||
|
|
||||||
|
if (result == 4) {
|
||||||
|
context->externalCoroutineCount++;
|
||||||
|
context->c->externalCoroutine = true;
|
||||||
|
instructionPointer -= 3;
|
||||||
|
// PrintDebug("start external coroutine %ld\n", context->c->id);
|
||||||
|
goto awaitCommand;
|
||||||
|
} else if (context->c->externalCoroutine) {
|
||||||
|
context->externalCoroutineCount--;
|
||||||
|
context->c->externalCoroutine = false;
|
||||||
|
// PrintDebug("end external coroutine %ld\n", context->c->id);
|
||||||
|
}
|
||||||
|
|
||||||
if (result == 2 || result == 3) {
|
if (result == 2 || result == 3) {
|
||||||
if (context->c->stackPointer == context->c->stackEntriesAllocated) {
|
if (context->c->stackPointer == context->c->stackEntriesAllocated) {
|
||||||
PrintDebug("Evaluation stack overflow.\n");
|
PrintDebug("Evaluation stack overflow.\n");
|
||||||
|
@ -4304,6 +4340,11 @@ bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *impo
|
||||||
context->rootNode = ParseRoot(&tokenizer);
|
context->rootNode = ParseRoot(&tokenizer);
|
||||||
context->functionData->importData = tokenizer.module;
|
context->functionData->importData = tokenizer.module;
|
||||||
|
|
||||||
|
uint8_t b = 0; // Make sure no function can start at 0.
|
||||||
|
FunctionBuilderAppend(context->functionData, &b, sizeof(b));
|
||||||
|
b = T_AWAIT; // Put a T_AWAIT command at address 1.
|
||||||
|
FunctionBuilderAppend(context->functionData, &b, sizeof(b));
|
||||||
|
|
||||||
bool success = context->rootNode
|
bool success = context->rootNode
|
||||||
&& ASTSetScopes(&tokenizer, context, context->rootNode, NULL)
|
&& ASTSetScopes(&tokenizer, context, context->rootNode, NULL)
|
||||||
&& ASTLookupTypeIdentifiers(&tokenizer, context->rootNode)
|
&& ASTLookupTypeIdentifiers(&tokenizer, context->rootNode)
|
||||||
|
@ -4438,6 +4479,9 @@ void ScriptFree(ExecutionContext *context) {
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
#endif
|
#endif
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
@ -4451,6 +4495,10 @@ uint8_t *fixedAllocationCurrentBlock;
|
||||||
uintptr_t fixedAllocationCurrentPosition;
|
uintptr_t fixedAllocationCurrentPosition;
|
||||||
size_t fixedAllocationCurrentSize;
|
size_t fixedAllocationCurrentSize;
|
||||||
|
|
||||||
|
sem_t externalCoroutineSemaphore;
|
||||||
|
pthread_mutex_t externalCoroutineMutex;
|
||||||
|
CoroutineState *externalCoroutineUnblockedList;
|
||||||
|
|
||||||
int ExternalStringTrim(ExecutionContext *context, Value *returnValue) {
|
int ExternalStringTrim(ExecutionContext *context, Value *returnValue) {
|
||||||
(void) returnValue;
|
(void) returnValue;
|
||||||
if (context->c->stackPointer < 1) return -1;
|
if (context->c->stackPointer < 1) return -1;
|
||||||
|
@ -4539,7 +4587,28 @@ int ExternalCharacterToByte(ExecutionContext *context, Value *returnValue) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExternalCoroutineDone(CoroutineState *coroutine) {
|
||||||
|
sem_post(&externalCoroutineSemaphore);
|
||||||
|
pthread_mutex_lock(&externalCoroutineMutex);
|
||||||
|
coroutine->nextUnblockedCoroutine = externalCoroutineUnblockedList;
|
||||||
|
externalCoroutineUnblockedList = coroutine;
|
||||||
|
pthread_mutex_unlock(&externalCoroutineMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *SystemShellExecuteThread(void *_coroutine) {
|
||||||
|
CoroutineState *coroutine = (CoroutineState *) _coroutine;
|
||||||
|
coroutine->externalCoroutineData.i = system((char *) coroutine->externalCoroutineData2) == 0;
|
||||||
|
free((char *) coroutine->externalCoroutineData2);
|
||||||
|
ExternalCoroutineDone(coroutine);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int ExternalSystemShellExecute(ExecutionContext *context, Value *returnValue) {
|
int ExternalSystemShellExecute(ExecutionContext *context, Value *returnValue) {
|
||||||
|
if (context->c->externalCoroutine) {
|
||||||
|
*returnValue = context->c->externalCoroutineData;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (context->c->stackPointer < 1) return -1;
|
if (context->c->stackPointer < 1) return -1;
|
||||||
uint64_t index = context->c->stack[--context->c->stackPointer].i;
|
uint64_t index = context->c->stack[--context->c->stackPointer].i;
|
||||||
if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
|
if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
|
||||||
|
@ -4554,17 +4623,32 @@ int ExternalSystemShellExecute(ExecutionContext *context, Value *returnValue) {
|
||||||
memcpy(temporary, text, bytes);
|
memcpy(temporary, text, bytes);
|
||||||
temporary[bytes] = 0;
|
temporary[bytes] = 0;
|
||||||
PrintDebug("\033[0;32m%s\033[0m\n", temporary);
|
PrintDebug("\033[0;32m%s\033[0m\n", temporary);
|
||||||
returnValue->i = system(temporary) == 0;
|
context->c->externalCoroutineData2 = temporary;
|
||||||
free(temporary);
|
pthread_t thread;
|
||||||
|
pthread_create(&thread, NULL, SystemShellExecuteThread, context->c);
|
||||||
|
return 4;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error in ExternalSystemShellExecute: Out of memory.\n");
|
fprintf(stderr, "Error in ExternalSystemShellExecute: Out of memory.\n");
|
||||||
returnValue->i = 0;
|
returnValue->i = 0;
|
||||||
}
|
|
||||||
|
|
||||||
return 2;
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *SystemShellExecuteWithWorkingDirectoryThread(void *_coroutine) {
|
||||||
|
CoroutineState *coroutine = (CoroutineState *) _coroutine;
|
||||||
|
int status;
|
||||||
|
Assert(coroutine->externalCoroutineData.i == waitpid(coroutine->externalCoroutineData.i, &status, 0));
|
||||||
|
coroutine->externalCoroutineData.i = WEXITSTATUS(status) == 0;
|
||||||
|
ExternalCoroutineDone(coroutine);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExternalSystemShellExecuteWithWorkingDirectory(ExecutionContext *context, Value *returnValue) {
|
int ExternalSystemShellExecuteWithWorkingDirectory(ExecutionContext *context, Value *returnValue) {
|
||||||
|
if (context->c->externalCoroutine) {
|
||||||
|
*returnValue = context->c->externalCoroutineData;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (context->c->stackPointer < 2) return -1;
|
if (context->c->stackPointer < 2) return -1;
|
||||||
uint64_t index = context->c->stack[--context->c->stackPointer].i;
|
uint64_t index = context->c->stack[--context->c->stackPointer].i;
|
||||||
if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
|
if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
|
||||||
|
@ -4586,22 +4670,28 @@ int ExternalSystemShellExecuteWithWorkingDirectory(ExecutionContext *context, Va
|
||||||
memcpy(temporary2, entry2->text, entry2->bytes);
|
memcpy(temporary2, entry2->text, entry2->bytes);
|
||||||
temporary2[entry2->bytes] = 0;
|
temporary2[entry2->bytes] = 0;
|
||||||
|
|
||||||
char *data = (char *) malloc(10000);
|
|
||||||
|
|
||||||
if (!data || data != getcwd(data, 10000)) {
|
|
||||||
PrintError4(context, 0, "Could not get the working directory.\n");
|
|
||||||
free(data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
chdir(temporary);
|
|
||||||
PrintDebug("\033[0;32m(%s) %s\033[0m\n", temporary, temporary2);
|
PrintDebug("\033[0;32m(%s) %s\033[0m\n", temporary, temporary2);
|
||||||
returnValue->i = system(temporary2) == 0;
|
|
||||||
chdir(data);
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
chdir(temporary);
|
||||||
|
exit(system(temporary2));
|
||||||
|
} else if (pid < 0) {
|
||||||
|
PrintDebug("Unable to fork(), got pid = %d, errno = %d.\n", pid, errno);
|
||||||
|
returnValue->i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
free(temporary);
|
free(temporary);
|
||||||
free(temporary2);
|
free(temporary2);
|
||||||
free(data);
|
|
||||||
|
if (pid > 0) {
|
||||||
|
context->c->externalCoroutineData.i = pid;
|
||||||
|
pthread_t thread;
|
||||||
|
pthread_create(&thread, NULL, SystemShellExecuteWithWorkingDirectoryThread, context->c);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5272,6 +5362,17 @@ int ExternalSystemGetHostName(ExecutionContext *context, Value *returnValue) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context) {
|
||||||
|
while (sem_wait(&externalCoroutineSemaphore) == -1);
|
||||||
|
pthread_mutex_lock(&externalCoroutineMutex);
|
||||||
|
CoroutineState *unblocked = externalCoroutineUnblockedList;
|
||||||
|
Assert(unblocked);
|
||||||
|
externalCoroutineUnblockedList = unblocked->nextUnblockedCoroutine;
|
||||||
|
unblocked->nextUnblockedCoroutine = NULL;
|
||||||
|
pthread_mutex_unlock(&externalCoroutineMutex);
|
||||||
|
return unblocked;
|
||||||
|
}
|
||||||
|
|
||||||
void *AllocateFixed(size_t bytes) {
|
void *AllocateFixed(size_t bytes) {
|
||||||
if (!bytes) {
|
if (!bytes) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -5465,6 +5566,8 @@ int main(int argc, char **argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sem_init(&externalCoroutineSemaphore, 0, 0);
|
||||||
|
|
||||||
options = argv + 2;
|
options = argv + 2;
|
||||||
optionCount = argc - 2;
|
optionCount = argc - 2;
|
||||||
optionsMatched = (bool *) calloc(argc - 2, sizeof(bool));
|
optionsMatched = (bool *) calloc(argc - 2, sizeof(bool));
|
||||||
|
|
Loading…
Reference in New Issue