mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
remove threaded vm
remove sleep
This commit is contained in:
parent
3e8d35f8d8
commit
7ad0ed1e2e
@ -1 +1 @@
|
|||||||
g++ -o pocketpy src/main.cpp --std=c++17 -O1 -pthread -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti
|
g++ -o pocketpy src/main.cpp --std=c++17 -O1 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti
|
@ -34,8 +34,6 @@
|
|||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#else
|
|
||||||
#include <thread>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PK_VERSION "0.6.2"
|
#define PK_VERSION "0.6.2"
|
||||||
|
50
src/main.cpp
50
src/main.cpp
@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
#include "pocketpy.h"
|
#include "pocketpy.h"
|
||||||
|
|
||||||
#define PK_DEBUG_TIME
|
//#define PK_DEBUG_TIME
|
||||||
//#define PK_DEBUG_THREADED
|
|
||||||
|
|
||||||
struct Timer{
|
struct Timer{
|
||||||
const char* title;
|
const char* title;
|
||||||
@ -22,53 +21,20 @@ struct Timer{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void _tvm_dispatch(ThreadedVM* vm){
|
|
||||||
while(pkpy_tvm_get_state(vm) != THREAD_FINISHED){
|
|
||||||
if(pkpy_tvm_get_state(vm) == THREAD_SUSPENDED){
|
|
||||||
char* obj = pkpy_tvm_read_jsonrpc_request(vm);
|
|
||||||
// this is not safe, but it's ok for demo
|
|
||||||
bool is_input_call = std::string_view(obj).find("\"input\"") != std::string::npos;
|
|
||||||
if(is_input_call){
|
|
||||||
std::string line;
|
|
||||||
std::getline(std::cin, line);
|
|
||||||
_StrStream ss;
|
|
||||||
ss << '{';
|
|
||||||
ss << "\"result\": " << _Str(line).__escape(false);
|
|
||||||
ss << '}';
|
|
||||||
pkpy_tvm_write_jsonrpc_response(vm, ss.str().c_str());
|
|
||||||
}else{
|
|
||||||
std::cout << "unknown jsonrpc call" << std::endl;
|
|
||||||
std::cout << obj << std::endl;
|
|
||||||
exit(3);
|
|
||||||
}
|
|
||||||
pkpy_delete(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __NO_MAIN
|
#ifndef __NO_MAIN
|
||||||
|
|
||||||
int main(int argc, char** argv){
|
int main(int argc, char** argv){
|
||||||
if(argc == 1){
|
if(argc == 1){
|
||||||
#ifndef PK_DEBUG_THREADED
|
|
||||||
VM* vm = pkpy_new_vm(true);
|
VM* vm = pkpy_new_vm(true);
|
||||||
#else
|
|
||||||
ThreadedVM* vm = pkpy_new_tvm(true);
|
|
||||||
#endif
|
|
||||||
REPL repl(vm);
|
REPL repl(vm);
|
||||||
int result = -1;
|
int result = -1;
|
||||||
while(true){
|
while(true){
|
||||||
(*vm->_stdout) << (result==0 ? "... " : ">>> ");
|
(*vm->_stdout) << (result==0 ? "... " : ">>> ");
|
||||||
std::string line;
|
std::string line;
|
||||||
std::getline(std::cin, line);
|
std::getline(std::cin, line);
|
||||||
pkpy_repl_input(&repl, line.c_str());
|
result = pkpy_repl_input(&repl, line.c_str());
|
||||||
result = pkpy_repl_last_input_result(&repl);
|
|
||||||
#ifdef PK_DEBUG_THREADED
|
|
||||||
if(result == (int)EXEC_STARTED){
|
|
||||||
_tvm_dispatch(vm);
|
|
||||||
pkpy_tvm_reset_state(vm);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -84,18 +50,10 @@ int main(int argc, char** argv){
|
|||||||
}
|
}
|
||||||
std::string src((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
std::string src((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
ThreadedVM* vm = pkpy_new_tvm(true);
|
VM* vm = pkpy_new_vm(true);
|
||||||
#ifdef PK_DEBUG_THREADED
|
|
||||||
Timer("Running time").run([=]{
|
|
||||||
vm->execAsync(src.c_str(), filename, EXEC_MODE);
|
|
||||||
_tvm_dispatch(vm);
|
|
||||||
});
|
|
||||||
#else
|
|
||||||
Timer("Running time").run([=]{
|
Timer("Running time").run([=]{
|
||||||
vm->exec(src.c_str(), filename, EXEC_MODE);
|
vm->exec(src.c_str(), filename, EXEC_MODE);
|
||||||
});
|
});
|
||||||
#endif
|
|
||||||
|
|
||||||
pkpy_delete(vm);
|
pkpy_delete(vm);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
20
src/parser.h
20
src/parser.h
@ -28,9 +28,7 @@ constexpr _TokenType TK(const char* const token) {
|
|||||||
for(int k=0; k<__TOKENS_LEN; k++){
|
for(int k=0; k<__TOKENS_LEN; k++){
|
||||||
const char* i = __TOKENS[k];
|
const char* i = __TOKENS[k];
|
||||||
const char* j = token;
|
const char* j = token;
|
||||||
while(*i && *j && *i == *j){
|
while(*i && *j && *i == *j) { i++; j++;}
|
||||||
i++; j++;
|
|
||||||
}
|
|
||||||
if(*i == *j) return k;
|
if(*i == *j) return k;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -101,9 +99,7 @@ struct Parser {
|
|||||||
std::queue<Token> nexts;
|
std::queue<Token> nexts;
|
||||||
std::stack<int> indents;
|
std::stack<int> indents;
|
||||||
|
|
||||||
int brackets_level_0 = 0;
|
int brackets_level = 0;
|
||||||
int brackets_level_1 = 0;
|
|
||||||
int brackets_level_2 = 0;
|
|
||||||
|
|
||||||
Token next_token(){
|
Token next_token(){
|
||||||
if(nexts.empty()){
|
if(nexts.empty()){
|
||||||
@ -143,7 +139,7 @@ struct Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool eat_indentation(){
|
bool eat_indentation(){
|
||||||
if(brackets_level_0 > 0 || brackets_level_1 > 0 || brackets_level_2 > 0) return true;
|
if(brackets_level > 0) return true;
|
||||||
int spaces = eat_spaces();
|
int spaces = eat_spaces();
|
||||||
if(peekchar() == '#') skip_line_comment();
|
if(peekchar() == '#') skip_line_comment();
|
||||||
if(peekchar() == '\0' || peekchar() == '\n') return true;
|
if(peekchar() == '\0' || peekchar() == '\n') return true;
|
||||||
@ -272,16 +268,10 @@ struct Parser {
|
|||||||
|
|
||||||
// Initialize the next token as the type.
|
// Initialize the next token as the type.
|
||||||
void set_next_token(_TokenType type, PyVar value=nullptr) {
|
void set_next_token(_TokenType type, PyVar value=nullptr) {
|
||||||
|
|
||||||
switch(type){
|
switch(type){
|
||||||
case TK("("): brackets_level_0++; break;
|
case TK("{"): case TK("["): case TK("("): brackets_level++; break;
|
||||||
case TK(")"): brackets_level_0--; break;
|
case TK(")"): case TK("]"): case TK("}"): brackets_level--; break;
|
||||||
case TK("["): brackets_level_1++; break;
|
|
||||||
case TK("]"): brackets_level_1--; break;
|
|
||||||
case TK("{"): brackets_level_2++; break;
|
|
||||||
case TK("}"): brackets_level_2--; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nexts.push( Token{
|
nexts.push( Token{
|
||||||
type,
|
type,
|
||||||
token_start,
|
token_start,
|
||||||
|
@ -610,16 +610,6 @@ void __addModuleTime(VM* vm){
|
|||||||
auto now = std::chrono::high_resolution_clock::now();
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
return vm->PyFloat(std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count() / 1000000.0);
|
return vm->PyFloat(std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count() / 1000000.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
vm->bindFunc(mod, "sleep", [](VM* vm, const pkpy::ArgList& args) {
|
|
||||||
vm->check_args_size(args, 1);
|
|
||||||
if(!vm->is_int_or_float(args[0])){
|
|
||||||
vm->typeError("time.sleep() argument must be int or float");
|
|
||||||
}
|
|
||||||
double sec = vm->num_to_float(args[0]);
|
|
||||||
vm->sleepForSecs(sec);
|
|
||||||
return vm->None;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __addModuleSys(VM* vm){
|
void __addModuleSys(VM* vm){
|
||||||
@ -833,7 +823,7 @@ public:
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
__EXPORT
|
__EXPORT
|
||||||
/// Delete a pointer allocated by `pkpy_xxx_xxx`.
|
/// Delete a pointer allocated by `pkpy_xxx_xxx`.
|
||||||
/// It can be `VM*`, `REPL*`, `ThreadedVM*`, `char*`, etc.
|
/// It can be `VM*`, `REPL*`, `char*`, etc.
|
||||||
///
|
///
|
||||||
/// !!!
|
/// !!!
|
||||||
/// If the pointer is not allocated by `pkpy_xxx_xxx`, the behavior is undefined.
|
/// If the pointer is not allocated by `pkpy_xxx_xxx`, the behavior is undefined.
|
||||||
@ -896,14 +886,8 @@ extern "C" {
|
|||||||
|
|
||||||
__EXPORT
|
__EXPORT
|
||||||
/// Input a source line to an interactive console.
|
/// Input a source line to an interactive console.
|
||||||
void pkpy_repl_input(REPL* r, const char* line){
|
int pkpy_repl_input(REPL* r, const char* line){
|
||||||
r->input(line);
|
return r->input(line);
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Check if the REPL needs more lines.
|
|
||||||
int pkpy_repl_last_input_result(REPL* r){
|
|
||||||
return (int)(r->last_input_result());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__EXPORT
|
__EXPORT
|
||||||
@ -936,14 +920,6 @@ extern "C" {
|
|||||||
return vm;
|
return vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Create a virtual machine that supports asynchronous execution.
|
|
||||||
ThreadedVM* pkpy_new_tvm(bool use_stdio){
|
|
||||||
ThreadedVM* vm = pkpy_allocate(ThreadedVM, use_stdio);
|
|
||||||
__vm_init(vm);
|
|
||||||
return vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
__EXPORT
|
||||||
/// Read the standard output and standard error as string of a virtual machine.
|
/// Read the standard output and standard error as string of a virtual machine.
|
||||||
/// The `vm->use_stdio` should be `false`.
|
/// The `vm->use_stdio` should be `false`.
|
||||||
@ -964,48 +940,4 @@ extern "C" {
|
|||||||
s_err->str("");
|
s_err->str("");
|
||||||
return strdup(ss.str().c_str());
|
return strdup(ss.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Get the current state of a threaded virtual machine.
|
|
||||||
///
|
|
||||||
/// Return `0` for `THREAD_READY`,
|
|
||||||
/// `1` for `THREAD_RUNNING`,
|
|
||||||
/// `2` for `THREAD_SUSPENDED`,
|
|
||||||
/// `3` for `THREAD_FINISHED`.
|
|
||||||
int pkpy_tvm_get_state(ThreadedVM* vm){
|
|
||||||
return vm->getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Set the state of a threaded virtual machine to `THREAD_READY`.
|
|
||||||
/// The current state should be `THREAD_FINISHED`.
|
|
||||||
void pkpy_tvm_reset_state(ThreadedVM* vm){
|
|
||||||
vm->resetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Read the current JSONRPC request from shared string buffer.
|
|
||||||
char* pkpy_tvm_read_jsonrpc_request(ThreadedVM* vm){
|
|
||||||
_Str s = vm->readJsonRpcRequest();
|
|
||||||
return strdup(s.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Write a JSONRPC response to shared string buffer.
|
|
||||||
void pkpy_tvm_write_jsonrpc_response(ThreadedVM* vm, const char* value){
|
|
||||||
vm->writeJsonrpcResponse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Emit a KeyboardInterrupt signal to stop a running threaded virtual machine.
|
|
||||||
void pkpy_tvm_terminate(ThreadedVM* vm){
|
|
||||||
vm->terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
__EXPORT
|
|
||||||
/// Run a given source on a threaded virtual machine.
|
|
||||||
/// The excution will be started in a new thread.
|
|
||||||
void pkpy_tvm_exec_async(VM* vm, const char* source){
|
|
||||||
vm->execAsync(source, "main.py", EXEC_MODE);
|
|
||||||
}
|
|
||||||
}
|
}
|
26
src/repl.h
26
src/repl.h
@ -14,7 +14,6 @@ protected:
|
|||||||
int need_more_lines = 0;
|
int need_more_lines = 0;
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
VM* vm;
|
VM* vm;
|
||||||
InputResult lastResult = EXEC_SKIPPED;
|
|
||||||
public:
|
public:
|
||||||
REPL(VM* vm) : vm(vm){
|
REPL(VM* vm) : vm(vm){
|
||||||
(*vm->_stdout) << ("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ")\n");
|
(*vm->_stdout) << ("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ")\n");
|
||||||
@ -22,11 +21,7 @@ public:
|
|||||||
(*vm->_stdout) << ("Type \"exit()\" to exit." "\n");
|
(*vm->_stdout) << ("Type \"exit()\" to exit." "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
InputResult last_input_result() const {
|
InputResult input(std::string line){
|
||||||
return lastResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
void input(std::string line){
|
|
||||||
if(need_more_lines){
|
if(need_more_lines){
|
||||||
buffer += line;
|
buffer += line;
|
||||||
buffer += '\n';
|
buffer += '\n';
|
||||||
@ -40,15 +35,10 @@ public:
|
|||||||
buffer.clear();
|
buffer.clear();
|
||||||
}else{
|
}else{
|
||||||
__NOT_ENOUGH_LINES:
|
__NOT_ENOUGH_LINES:
|
||||||
lastResult = NEED_MORE_LINES;
|
return NEED_MORE_LINES;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(line == "exit()") exit(0);
|
if(line.empty()) return EXEC_SKIPPED;
|
||||||
if(line.empty()) {
|
|
||||||
lastResult = EXEC_SKIPPED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
@ -58,15 +48,11 @@ __NOT_ENOUGH_LINES:
|
|||||||
buffer += line;
|
buffer += line;
|
||||||
buffer += '\n';
|
buffer += '\n';
|
||||||
need_more_lines = ne.isClassDef ? 3 : 2;
|
need_more_lines = ne.isClassDef ? 3 : 2;
|
||||||
if (need_more_lines) {
|
if (need_more_lines) return NEED_MORE_LINES;
|
||||||
lastResult = NEED_MORE_LINES;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}catch(...){
|
}catch(...){
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
vm->exec(line, "<stdin>", SINGLE_MODE);
|
||||||
lastResult = EXEC_STARTED;
|
return EXEC_STARTED;
|
||||||
vm->execAsync(line, "<stdin>", SINGLE_MODE);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
160
src/vm.h
160
src/vm.h
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
|
|
||||||
class VM {
|
class VM {
|
||||||
std::atomic<bool> _stop_flag = false;
|
|
||||||
std::vector<PyVar> _small_integers; // [-5, 256]
|
std::vector<PyVar> _small_integers; // [-5, 256]
|
||||||
PyVarDict _modules; // loaded modules
|
PyVarDict _modules; // loaded modules
|
||||||
emhash8::HashMap<_Str, _Str> _lazy_modules; // lazy loaded modules
|
emhash8::HashMap<_Str, _Str> _lazy_modules; // lazy loaded modules
|
||||||
@ -29,21 +28,11 @@ protected:
|
|||||||
std::deque< std::unique_ptr<Frame> > callstack;
|
std::deque< std::unique_ptr<Frame> > callstack;
|
||||||
PyVar __py2py_call_signal;
|
PyVar __py2py_call_signal;
|
||||||
|
|
||||||
inline void test_stop_flag(){
|
|
||||||
if(_stop_flag){
|
|
||||||
_stop_flag = false;
|
|
||||||
_error("KeyboardInterrupt", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PyVar run_frame(Frame* frame){
|
PyVar run_frame(Frame* frame){
|
||||||
while(frame->has_next_bytecode()){
|
while(frame->has_next_bytecode()){
|
||||||
const Bytecode& byte = frame->next_bytecode();
|
const Bytecode& byte = frame->next_bytecode();
|
||||||
//printf("[%d] %s (%d)\n", frame->stack_size(), OP_NAMES[byte.op], byte.arg);
|
//printf("[%d] %s (%d)\n", frame->stack_size(), OP_NAMES[byte.op], byte.arg);
|
||||||
//printf("%s\n", frame->code->src->getLine(byte.line).c_str());
|
//printf("%s\n", frame->code->src->getLine(byte.line).c_str());
|
||||||
|
|
||||||
test_stop_flag();
|
|
||||||
|
|
||||||
switch (byte.op)
|
switch (byte.op)
|
||||||
{
|
{
|
||||||
case OP_NO_OP: break; // do nothing
|
case OP_NO_OP: break; // do nothing
|
||||||
@ -380,22 +369,6 @@ public:
|
|||||||
for(i64 i=-5; i<=256; i++) _small_integers.push_back(new_object(_tp_int, i));
|
for(i64 i=-5; i<=256; i++) _small_integers.push_back(new_object(_tp_int, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboardInterrupt(){
|
|
||||||
_stop_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleepForSecs(f64 sec){
|
|
||||||
i64 ms = (i64)(sec * 1000);
|
|
||||||
for(i64 i=0; i<ms; i+=20){
|
|
||||||
test_stop_flag();
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
emscripten_sleep(20);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PyVar asStr(const PyVar& obj){
|
PyVar asStr(const PyVar& obj){
|
||||||
PyVarOrNull str_fn = getattr(obj, __str__, false);
|
PyVarOrNull str_fn = getattr(obj, __str__, false);
|
||||||
if(str_fn != nullptr) return call(str_fn);
|
if(str_fn != nullptr) return call(str_fn);
|
||||||
@ -566,10 +539,6 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void execAsync(_Str source, _Str filename, CompileMode mode) {
|
|
||||||
exec(source, filename, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Frame* __pushNewFrame(const _Code& code, PyVar _module, PyVarDict&& locals){
|
Frame* __pushNewFrame(const _Code& code, PyVar _module, PyVarDict&& locals){
|
||||||
if(code == nullptr) UNREACHABLE();
|
if(code == nullptr) UNREACHABLE();
|
||||||
if(callstack.size() > maxRecursionDepth){
|
if(callstack.size() > maxRecursionDepth){
|
||||||
@ -737,7 +706,8 @@ public:
|
|||||||
}else if(obj->is_type(_tp_float)){
|
}else if(obj->is_type(_tp_float)){
|
||||||
return PyFloat_AS_C(obj);
|
return PyFloat_AS_C(obj);
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
typeError("expected int or float");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyVar num_negated(const PyVar& obj){
|
PyVar num_negated(const PyVar& obj){
|
||||||
@ -925,9 +895,7 @@ public:
|
|||||||
i64 x = 1000003;
|
i64 x = 1000003;
|
||||||
for (const auto& item : PyTuple_AS_C(obj)) {
|
for (const auto& item : PyTuple_AS_C(obj)) {
|
||||||
i64 y = hash(item);
|
i64 y = hash(item);
|
||||||
// this is recommended by Github Copilot
|
x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2)); // recommended by Github Copilot
|
||||||
// i am not sure whether it is a good idea
|
|
||||||
x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2));
|
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -1023,10 +991,10 @@ void NameRef::del(VM* vm, Frame* frame) const{
|
|||||||
} break;
|
} break;
|
||||||
case NAME_GLOBAL:
|
case NAME_GLOBAL:
|
||||||
{
|
{
|
||||||
if(frame->f_locals.count(pair->first) > 0){
|
if(frame->f_locals.contains(pair->first)){
|
||||||
frame->f_locals.erase(pair->first);
|
frame->f_locals.erase(pair->first);
|
||||||
}else{
|
}else{
|
||||||
if(frame->f_globals().count(pair->first) > 0){
|
if(frame->f_globals().contains(pair->first)){
|
||||||
frame->f_globals().erase(pair->first);
|
frame->f_globals().erase(pair->first);
|
||||||
}else{
|
}else{
|
||||||
vm->nameError(pair->first);
|
vm->nameError(pair->first);
|
||||||
@ -1100,121 +1068,3 @@ PyVar RangeIterator::next(){
|
|||||||
PyVar StringIterator::next(){
|
PyVar StringIterator::next(){
|
||||||
return vm->PyStr(str.u8_getitem(index++));
|
return vm->PyStr(str.u8_getitem(index++));
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ThreadState {
|
|
||||||
THREAD_READY,
|
|
||||||
THREAD_RUNNING,
|
|
||||||
THREAD_SUSPENDED,
|
|
||||||
THREAD_FINISHED
|
|
||||||
};
|
|
||||||
|
|
||||||
class ThreadedVM : public VM {
|
|
||||||
std::atomic<ThreadState> _state = THREAD_READY;
|
|
||||||
_Str _sharedStr = "";
|
|
||||||
|
|
||||||
#ifndef __EMSCRIPTEN__
|
|
||||||
std::thread* _thread = nullptr;
|
|
||||||
void __deleteThread(){
|
|
||||||
if(_thread != nullptr){
|
|
||||||
terminate();
|
|
||||||
_thread->join();
|
|
||||||
delete _thread;
|
|
||||||
_thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void __deleteThread(){
|
|
||||||
terminate();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
ThreadedVM(bool use_stdio) : VM(use_stdio) {
|
|
||||||
bindBuiltinFunc("__string_channel_call", [](VM* vm, const pkpy::ArgList& args){
|
|
||||||
vm->check_args_size(args, 1);
|
|
||||||
_Str data = vm->PyStr_AS_C(args[0]);
|
|
||||||
|
|
||||||
ThreadedVM* tvm = (ThreadedVM*)vm;
|
|
||||||
tvm->_sharedStr = data;
|
|
||||||
tvm->suspend();
|
|
||||||
return tvm->PyStr(tvm->readJsonRpcRequest());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void terminate(){
|
|
||||||
if(_state == THREAD_RUNNING || _state == THREAD_SUSPENDED){
|
|
||||||
keyboardInterrupt();
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// no way to terminate safely
|
|
||||||
#else
|
|
||||||
while(_state != THREAD_FINISHED);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void suspend(){
|
|
||||||
if(_state != THREAD_RUNNING) UNREACHABLE();
|
|
||||||
_state = THREAD_SUSPENDED;
|
|
||||||
while(_state == THREAD_SUSPENDED){
|
|
||||||
test_stop_flag();
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
emscripten_sleep(20);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_Str readJsonRpcRequest(){
|
|
||||||
_Str copy = _sharedStr;
|
|
||||||
_sharedStr = "";
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***** For outer use *****/
|
|
||||||
|
|
||||||
ThreadState getState(){
|
|
||||||
return _state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeJsonrpcResponse(const char* value){
|
|
||||||
if(_state != THREAD_SUSPENDED) UNREACHABLE();
|
|
||||||
_sharedStr = _Str(value);
|
|
||||||
_state = THREAD_RUNNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
void execAsync(_Str source, _Str filename, CompileMode mode) override {
|
|
||||||
if(_state != THREAD_READY) UNREACHABLE();
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
this->_state = THREAD_RUNNING;
|
|
||||||
VM::exec(source, filename, mode);
|
|
||||||
this->_state = THREAD_FINISHED;
|
|
||||||
#else
|
|
||||||
__deleteThread();
|
|
||||||
_thread = new std::thread([=](){
|
|
||||||
this->_state = THREAD_RUNNING;
|
|
||||||
VM::exec(source, filename, mode);
|
|
||||||
this->_state = THREAD_FINISHED;
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PyVarOrNull exec(_Str source, _Str filename, CompileMode mode, PyVar _module=nullptr) override {
|
|
||||||
if(_state == THREAD_READY) return VM::exec(source, filename, mode, _module);
|
|
||||||
auto callstackBackup = std::move(callstack);
|
|
||||||
callstack.clear();
|
|
||||||
PyVarOrNull ret = VM::exec(source, filename, mode, _module);
|
|
||||||
callstack = std::move(callstackBackup);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetState(){
|
|
||||||
if(this->_state != THREAD_FINISHED) return;
|
|
||||||
this->_state = THREAD_READY;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ThreadedVM(){
|
|
||||||
__deleteThread();
|
|
||||||
}
|
|
||||||
};
|
|
@ -34,8 +34,7 @@ function term_init() {
|
|||||||
term.write("Bye!\r\n");
|
term.write("Bye!\r\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Module.ccall('pkpy_repl_input', 'number', ['number', 'string'], [repl, command]);
|
need_more_lines = Module.ccall('pkpy_repl_input', 'number', ['number', 'string'], [repl, command]) == 0;
|
||||||
need_more_lines = Module.ccall('pkpy_repl_last_input_result', 'number', ['number'], [repl]) == 0;
|
|
||||||
command = '';
|
command = '';
|
||||||
term.write(need_more_lines ? "... " : ">>> ");
|
term.write(need_more_lines ? "... " : ">>> ");
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user