diff options
Diffstat (limited to 'unix')
-rw-r--r-- | unix/Makefile | 38 | ||||
-rw-r--r-- | unix/alloc.c | 23 | ||||
-rw-r--r-- | unix/file.c | 12 | ||||
-rw-r--r-- | unix/gccollect.c | 18 | ||||
-rw-r--r-- | unix/main.c | 26 | ||||
-rw-r--r-- | unix/modmachine.c | 7 | ||||
-rw-r--r-- | unix/modsocket.c | 2 | ||||
-rw-r--r-- | unix/modtime.c | 13 | ||||
-rw-r--r-- | unix/mpconfigport.h | 21 | ||||
-rw-r--r-- | unix/mpconfigport.mk | 11 | ||||
-rw-r--r-- | unix/mpconfigport_coverage.h | 4 | ||||
-rw-r--r-- | unix/mpconfigport_minimal.h | 30 | ||||
-rw-r--r-- | unix/mpthreadport.c | 223 | ||||
-rw-r--r-- | unix/mpthreadport.h | 36 |
14 files changed, 410 insertions, 54 deletions
diff --git a/unix/Makefile b/unix/Makefile index 90653e88e8..2e0cbfd35a 100644 --- a/unix/Makefile +++ b/unix/Makefile @@ -28,7 +28,7 @@ ifdef DEBUG CFLAGS += -g COPT = -O0 else -COPT = -Os #-DNDEBUG +COPT = -Os -fdata-sections -ffunction-sections #-DNDEBUG # _FORTIFY_SOURCE is a feature in gcc/glibc which is intended to provide extra # security for detecting buffer overflows. Some distros (Ubuntu at the very least) # have it enabled by default. @@ -63,10 +63,10 @@ endif ifeq ($(UNAME_S),Darwin) CC = clang # Use clang syntax for map file -LDFLAGS_ARCH = -Wl,-map,$@.map +LDFLAGS_ARCH = -Wl,-map,$@.map -Wl,-dead_strip else # Use gcc syntax for map file -LDFLAGS_ARCH = -Wl,-Map=$@.map,--cref +LDFLAGS_ARCH = -Wl,-Map=$@.map,--cref -Wl,--gc-sections endif LDFLAGS = $(LDFLAGS_MOD) $(LDFLAGS_ARCH) -lm $(LDFLAGS_EXTRA) @@ -85,16 +85,6 @@ INC += -I../lib/mp-readline CFLAGS_MOD += -DMICROPY_USE_READLINE=1 LIB_SRC_C_EXTRA += mp-readline/readline.c endif -ifeq ($(MICROPY_USE_READLINE),2) -CFLAGS_MOD += -DMICROPY_USE_READLINE=2 -LDFLAGS_MOD += -lreadline -# the following is needed for BSD -#LDFLAGS_MOD += -ltermcap -endif -ifeq ($(MICROPY_PY_TIME),1) -CFLAGS_MOD += -DMICROPY_PY_TIME=1 -SRC_MOD += modtime.c -endif ifeq ($(MICROPY_PY_TERMIOS),1) CFLAGS_MOD += -DMICROPY_PY_TERMIOS=1 SRC_MOD += modtermios.c @@ -103,6 +93,10 @@ ifeq ($(MICROPY_PY_SOCKET),1) CFLAGS_MOD += -DMICROPY_PY_SOCKET=1 SRC_MOD += modsocket.c endif +ifeq ($(MICROPY_PY_THREAD),1) +CFLAGS_MOD += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 +LDFLAGS_MOD += -lpthread +endif ifeq ($(MICROPY_PY_FFI),1) @@ -138,10 +132,12 @@ SRC_C = \ main.c \ gccollect.c \ unix_mphal.c \ + mpthreadport.c \ input.c \ file.c \ modmachine.c \ modos.c \ + modtime.c \ moduselect.c \ alloc.c \ coverage.c \ @@ -162,9 +158,13 @@ endif LIB_SRC_C = $(addprefix lib/,\ $(LIB_SRC_C_EXTRA) \ utils/printf.c \ + timeutils/timeutils.c \ + ) + +# FatFS VFS support +LIB_SRC_C += $(addprefix lib/,\ fatfs/ff.c \ fatfs/option/ccsbcs.c \ - timeutils/timeutils.c \ ) OBJ = $(PY_O) @@ -235,7 +235,11 @@ fast: # build a minimal interpreter minimal: - $(MAKE) COPT="-Os -DNDEBUG" CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_minimal.h>"' BUILD=build-minimal PROG=micropython_minimal MICROPY_PY_TIME=0 MICROPY_PY_TERMIOS=0 MICROPY_PY_SOCKET=0 MICROPY_PY_FFI=0 MICROPY_USE_READLINE=0 + $(MAKE) COPT="-Os -DNDEBUG" CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_minimal.h>"' \ + BUILD=build-minimal PROG=micropython_minimal \ + MICROPY_PY_BTREE=0 MICROPY_PY_FFI=0 MICROPY_PY_SOCKET=0 MICROPY_PY_THREAD=0 \ + MICROPY_PY_TERMIOS=0 MICROPY_PY_USSL=0 \ + MICROPY_USE_READLINE=0 MICROPY_FATFS=0 # build interpreter with nan-boxing as object model nanbox: @@ -260,7 +264,7 @@ freedos: # build an interpreter for coverage testing and do the testing coverage: - $(MAKE) COPT="-O0" CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_coverage.h>" -fprofile-arcs -ftest-coverage -Wdouble-promotion -Wformat -Wmissing-declarations -Wmissing-prototypes -Wold-style-definition -Wpointer-arith -Wshadow -Wsign-compare -Wuninitialized -Wunused-parameter -DMICROPY_UNIX_COVERAGE' LDFLAGS_EXTRA='-fprofile-arcs -ftest-coverage' BUILD=build-coverage PROG=micropython_coverage + $(MAKE) COPT="-O0" MICROPY_PY_BTREE=0 CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_coverage.h>" -fprofile-arcs -ftest-coverage -Wdouble-promotion -Wformat -Wmissing-declarations -Wmissing-prototypes -Wold-style-definition -Wpointer-arith -Wshadow -Wsign-compare -Wuninitialized -Wunused-parameter -DMICROPY_UNIX_COVERAGE' LDFLAGS_EXTRA='-fprofile-arcs -ftest-coverage' BUILD=build-coverage PROG=micropython_coverage coverage_test: coverage $(eval DIRNAME=$(notdir $(CURDIR))) @@ -301,7 +305,7 @@ libffi: cd ../lib/libffi; git clean -d -x -f cd ../lib/libffi; ./autogen.sh mkdir -p ../lib/libffi/build_dir; cd ../lib/libffi/build_dir; \ - ../configure $(CROSS_COMPILE_HOST) --prefix=$$PWD/out CC="$(CC)" CXX="$(CXX)" LD="$(LD)"; \ + ../configure $(CROSS_COMPILE_HOST) --prefix=$$PWD/out --disable-structs CC="$(CC)" CXX="$(CXX)" LD="$(LD)" CFLAGS="-Os -fomit-frame-pointer -fstrict-aliasing -ffast-math -fno-exceptions"; \ make install-exec-recursive; make -C include install-data-am axtls: ../lib/axtls/README diff --git a/unix/alloc.c b/unix/alloc.c index a0676a0aef..04c635ee97 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -33,7 +33,7 @@ #include "py/mpstate.h" #include "py/gc.h" -#if MICROPY_EMIT_NATIVE +#if MICROPY_EMIT_NATIVE || (MICROPY_PY_FFI && MICROPY_FORCE_PLAT_ALLOC_EXEC) #if defined(__OpenBSD__) || defined(__MACH__) #define MAP_ANONYMOUS MAP_ANON @@ -85,4 +85,23 @@ void mp_unix_mark_exec(void) { } } -#endif // MICROPY_EMIT_NATIVE +#if MICROPY_FORCE_PLAT_ALLOC_EXEC +// Provide implementation of libffi ffi_closure_* functions in terms +// of the functions above. On a normal Linux system, this save a lot +// of code size. +void *ffi_closure_alloc(size_t size, void **code); +void ffi_closure_free(void *ptr); + +void *ffi_closure_alloc(size_t size, void **code) { + mp_uint_t dummy; + mp_unix_alloc_exec(size, code, &dummy); + return *code; +} + +void ffi_closure_free(void *ptr) { + (void)ptr; + // TODO +} +#endif + +#endif // MICROPY_EMIT_NATIVE || (MICROPY_PY_FFI && MICROPY_FORCE_PLAT_ALLOC_EXEC) diff --git a/unix/file.c b/unix/file.c index 33acdccd06..a7620e079e 100644 --- a/unix/file.c +++ b/unix/file.c @@ -88,6 +88,14 @@ STATIC mp_uint_t fdfile_write(mp_obj_t o_in, const void *buf, mp_uint_t size, in } #endif mp_int_t r = write(o->fd, buf, size); + while (r == -1 && errno == EINTR) { + if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + nlr_raise(obj); + } + r = write(o->fd, buf, size); + } if (r == -1) { *errcode = errno; return MP_STREAM_ERROR; @@ -242,7 +250,7 @@ const mp_obj_type_t mp_type_fileio = { .make_new = fdfile_make_new, .getiter = mp_identity, .iternext = mp_stream_unbuffered_iter, - .stream_p = &fileio_stream_p, + .protocol = &fileio_stream_p, .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, }; #endif @@ -261,7 +269,7 @@ const mp_obj_type_t mp_type_textio = { .make_new = fdfile_make_new, .getiter = mp_identity, .iternext = mp_stream_unbuffered_iter, - .stream_p = &textio_stream_p, + .protocol = &textio_stream_p, .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, }; diff --git a/unix/gccollect.c b/unix/gccollect.c index 125c273a30..397c4ffe1c 100644 --- a/unix/gccollect.c +++ b/unix/gccollect.c @@ -136,15 +136,25 @@ STATIC void gc_helper_get_regs(regs_t arr) { #endif // MICROPY_GCREGS_SETJMP -void gc_collect(void) { - //gc_dump_info(); +// this function is used by mpthreadport.c +void gc_collect_regs_and_stack(void); - gc_collect_start(); +void gc_collect_regs_and_stack(void) { regs_t regs; gc_helper_get_regs(regs); // GC stack (and regs because we captured them) void **regs_ptr = (void**)(void*)®s; - gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_VM(stack_top) - (uintptr_t)®s) / sizeof(uintptr_t)); + gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)®s) / sizeof(uintptr_t)); +} + +void gc_collect(void) { + //gc_dump_info(); + + gc_collect_start(); + gc_collect_regs_and_stack(); + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif #if MICROPY_EMIT_NATIVE mp_unix_mark_exec(); #endif diff --git a/unix/main.c b/unix/main.c index dedee28e7f..a1c057400f 100644 --- a/unix/main.c +++ b/unix/main.c @@ -45,6 +45,7 @@ #include "py/gc.h" #include "py/stackctrl.h" #include "py/mphal.h" +#include "py/mpthread.h" #include "extmod/misc.h" #include "genhdr/mpversion.h" #include "input.h" @@ -292,7 +293,7 @@ STATIC int usage(char **argv) { "-v : verbose (trace various operations); can be multiple\n" "-O[N] : apply bytecode optimizations of level N\n" "\n" -"Implementation specific options:\n", argv[0] +"Implementation specific options (-X):\n", argv[0] ); int impl_opts_cnt = 0; printf( @@ -302,7 +303,7 @@ STATIC int usage(char **argv) { impl_opts_cnt++; #if MICROPY_ENABLE_GC printf( -" heapsize=<n> -- set the heap size for the GC (default %ld)\n" +" heapsize=<n>[w][K|M] -- set the heap size for the GC (default %ld)\n" , heap_size); impl_opts_cnt++; #endif @@ -350,12 +351,20 @@ STATIC void pre_process_options(int argc, char **argv) { heap_size *= 1024; } else if ((*end | 0x20) == 'm') { heap_size *= 1024 * 1024; + } else { + // Compensate for ++ below + --end; + } + if (*++end != 0) { + goto invalid_arg; } if (word_adjust) { heap_size = heap_size * BYTES_PER_WORD / 4; } #endif } else { +invalid_arg: + printf("Invalid option\n"); exit(usage(argv)); } a++; @@ -379,6 +388,9 @@ STATIC void set_sys_argv(char *argv[], int argc, int start_arg) { MP_NOINLINE int main_(int argc, char **argv); int main(int argc, char **argv) { + #if MICROPY_PY_THREAD + mp_thread_init(); + #endif // We should capture stack top ASAP after start, and it should be // captured guaranteedly before any other stack variables are allocated. // For this, actual main (renamed main_) should not be inlined into @@ -432,10 +444,12 @@ MP_NOINLINE int main_(int argc, char **argv) { } if (p[0] == '~' && p[1] == '/' && home != NULL) { // Expand standalone ~ to $HOME - CHECKBUF(buf, PATH_MAX); - CHECKBUF_APPEND(buf, home, strlen(home)); - CHECKBUF_APPEND(buf, p + 1, (size_t)(p1 - p - 1)); - path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(buf, CHECKBUF_LEN(buf))); + int home_l = strlen(home); + vstr_t vstr; + vstr_init(&vstr, home_l + (p1 - p - 1) + 1); + vstr_add_strn(&vstr, home, home_l); + vstr_add_strn(&vstr, p + 1, p1 - p - 1); + path_items[i] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr); } else { path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(p, p1 - p)); } diff --git a/unix/modmachine.c b/unix/modmachine.c index 5c032c3466..166d47712d 100644 --- a/unix/modmachine.c +++ b/unix/modmachine.c @@ -31,6 +31,8 @@ #include "py/obj.h" #include "extmod/machine_mem.h" +#include "extmod/machine_pinbase.h" +#include "extmod/machine_pulse.h" #if MICROPY_PLAT_DEV_MEM #include <errno.h> @@ -78,6 +80,11 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + + { MP_ROM_QSTR(MP_QSTR_PinBase), MP_ROM_PTR(&machine_pinbase_type) }, + #if MICROPY_PY_MACHINE_PULSE + { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); diff --git a/unix/modsocket.c b/unix/modsocket.c index 8e0d86fc79..cd68b20a45 100644 --- a/unix/modsocket.c +++ b/unix/modsocket.c @@ -381,7 +381,7 @@ STATIC const mp_obj_type_t usocket_type = { .make_new = socket_make_new, .getiter = NULL, .iternext = NULL, - .stream_p = &usocket_stream_p, + .protocol = &usocket_stream_p, .locals_dict = (mp_obj_dict_t*)&usocket_locals_dict, }; diff --git a/unix/modtime.c b/unix/modtime.c index d3b780790c..6843238cf3 100644 --- a/unix/modtime.c +++ b/unix/modtime.c @@ -24,6 +24,9 @@ * THE SOFTWARE. */ +#include "py/mpconfig.h" +#if MICROPY_PY_UTIME + #include <unistd.h> #include <errno.h> #include <string.h> @@ -118,7 +121,9 @@ STATIC mp_obj_t mod_time_sleep(mp_obj_t arg) { tv.tv_sec = ipart; int res; while (1) { + MP_THREAD_GIL_EXIT(); res = sleep_select(0, NULL, NULL, NULL, &tv); + MP_THREAD_GIL_ENTER(); #if MICROPY_SELECT_REMAINING_TIME // TODO: This assumes Linux behavior of modifying tv to the remaining // time. @@ -136,20 +141,26 @@ STATIC mp_obj_t mod_time_sleep(mp_obj_t arg) { RAISE_ERRNO(res, errno); #else // TODO: Handle EINTR + MP_THREAD_GIL_EXIT(); sleep(mp_obj_get_int(arg)); + MP_THREAD_GIL_ENTER(); #endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_time_sleep_obj, mod_time_sleep); STATIC mp_obj_t mod_time_sleep_ms(mp_obj_t arg) { + MP_THREAD_GIL_EXIT(); usleep(mp_obj_get_int(arg) * 1000); + MP_THREAD_GIL_ENTER(); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_time_sleep_ms_obj, mod_time_sleep_ms); STATIC mp_obj_t mod_time_sleep_us(mp_obj_t arg) { + MP_THREAD_GIL_EXIT(); usleep(mp_obj_get_int(arg)); + MP_THREAD_GIL_ENTER(); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_time_sleep_us_obj, mod_time_sleep_us); @@ -190,3 +201,5 @@ const mp_obj_module_t mp_module_time = { .name = MP_QSTR_utime, .globals = (mp_obj_dict_t*)&mp_module_time_globals, }; + +#endif // MICROPY_PY_UTIME diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index 9601673a74..06c4edc1e2 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -103,6 +103,7 @@ #define MICROPY_STACKLESS_STRICT (0) #define MICROPY_PY_OS_STATVFS (1) +#define MICROPY_PY_UTIME (1) #define MICROPY_PY_UERRNO (1) #define MICROPY_PY_UCTYPES (1) #define MICROPY_PY_UZLIB (1) @@ -119,6 +120,7 @@ #define MICROPY_PY_USELECT (1) #endif #define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_MACHINE_MEM_GET_READ_ADDR mod_machine_mem_get_addr #define MICROPY_MACHINE_MEM_GET_WRITE_ADDR mod_machine_mem_get_addr @@ -129,8 +131,8 @@ #define MICROPY_FATFS_VOLUMES (3) #define MICROPY_FATFS_MAX_SS (4096) #define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ -#define MICROPY_FSUSERMOUNT (1) -#define MICROPY_VFS_FAT (1) +#define MICROPY_FSUSERMOUNT (0) +#define MICROPY_VFS_FAT (0) // Define to MICROPY_ERROR_REPORTING_DETAILED to get function, etc. // names in exception messages (may require more RAM). @@ -170,10 +172,10 @@ extern const struct _mp_obj_module_t mp_module_jni; #else #define MICROPY_PY_JNI_DEF #endif -#if MICROPY_PY_TIME -#define MICROPY_PY_TIME_DEF { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) }, +#if MICROPY_PY_UTIME +#define MICROPY_PY_UTIME_DEF { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) }, #else -#define MICROPY_PY_TIME_DEF +#define MICROPY_PY_UTIME_DEF #endif #if MICROPY_PY_TERMIOS #define MICROPY_PY_TERMIOS_DEF { MP_ROM_QSTR(MP_QSTR_termios), MP_ROM_PTR(&mp_module_termios) }, @@ -194,7 +196,7 @@ extern const struct _mp_obj_module_t mp_module_jni; #define MICROPY_PORT_BUILTIN_MODULES \ MICROPY_PY_FFI_DEF \ MICROPY_PY_JNI_DEF \ - MICROPY_PY_TIME_DEF \ + MICROPY_PY_UTIME_DEF \ MICROPY_PY_SOCKET_DEF \ { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, \ @@ -233,9 +235,10 @@ void mp_unix_free_exec(void *ptr, mp_uint_t size); void mp_unix_mark_exec(void); #define MP_PLAT_ALLOC_EXEC(min_size, ptr, size) mp_unix_alloc_exec(min_size, ptr, size) #define MP_PLAT_FREE_EXEC(ptr, size) mp_unix_free_exec(ptr, size) - -#ifndef MP_NOINLINE -#define MP_NOINLINE __attribute__((noinline)) +#ifndef MICROPY_FORCE_PLAT_ALLOC_EXEC +// Use MP_PLAT_ALLOC_EXEC for any executable memory allocation, including for FFI +// (overriding libffi own implementation) +#define MICROPY_FORCE_PLAT_ALLOC_EXEC (1) #endif #if MICROPY_PY_OS_DUPTERM diff --git a/unix/mpconfigport.mk b/unix/mpconfigport.mk index 88bd749a71..9f826661a9 100644 --- a/unix/mpconfigport.mk +++ b/unix/mpconfigport.mk @@ -6,11 +6,16 @@ MICROPY_FORCE_32BIT = 0 # This variable can take the following values: # 0 - no readline, just simple input # 1 - use MicroPython version of readline -# 2 - use GNU readline (causes binary to be licensed under GPL) MICROPY_USE_READLINE = 1 -# Subset of CPython time module -MICROPY_PY_TIME = 1 +# Whether to enable FatFs VFS +MICROPY_FATFS = 1 + +# btree module using Berkeley DB 1.xx +MICROPY_PY_BTREE = 1 + +# _thread module using pthreads +MICROPY_PY_THREAD = 1 # Subset of CPython termios module MICROPY_PY_TERMIOS = 1 diff --git a/unix/mpconfigport_coverage.h b/unix/mpconfigport_coverage.h index e5d5fd7a47..f9a6fbd9dd 100644 --- a/unix/mpconfigport_coverage.h +++ b/unix/mpconfigport_coverage.h @@ -31,3 +31,7 @@ #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_IO_BUFFEREDWRITER (1) +#undef MICROPY_FSUSERMOUNT +#undef MICROPY_VFS_FAT +#define MICROPY_FSUSERMOUNT (1) +#define MICROPY_VFS_FAT (1) diff --git a/unix/mpconfigport_minimal.h b/unix/mpconfigport_minimal.h index cffc9a6250..2ab95c67af 100644 --- a/unix/mpconfigport_minimal.h +++ b/unix/mpconfigport_minimal.h @@ -26,6 +26,12 @@ // options to control how Micro Python is built +#define MICROPY_ALLOC_QSTR_CHUNK_INIT (64) +#define MICROPY_ALLOC_PARSE_RULE_INIT (8) +#define MICROPY_ALLOC_PARSE_RULE_INC (8) +#define MICROPY_ALLOC_PARSE_RESULT_INIT (8) +#define MICROPY_ALLOC_PARSE_RESULT_INC (8) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (64) #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_FINALISER (0) @@ -38,6 +44,7 @@ #define MICROPY_ENABLE_SOURCE_LINE (0) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_WARNINGS (0) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) #define MICROPY_STREAMS_NON_BLOCK (0) @@ -76,6 +83,7 @@ #define MICROPY_PY_SYS_STDFILES (0) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_UCTYPES (0) +#define MICROPY_PY_UTIME (0) #define MICROPY_PY_UZLIB (0) #define MICROPY_PY_UJSON (0) #define MICROPY_PY_URE (0) @@ -83,6 +91,18 @@ #define MICROPY_PY_UHASHLIB (0) #define MICROPY_PY_UBINASCII (0) +extern const struct _mp_obj_module_t mp_module_os; + +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&mp_module_os }, \ + +#define MICROPY_PORT_ROOT_POINTERS \ + mp_obj_t keyboard_interrupt_obj; + +////////////////////////////////////////// +// Do not change anything beyond this line +////////////////////////////////////////// + // Define to 1 to use undertested inefficient GC helper implementation // (if more efficient arch-specific one is not available). #ifndef MICROPY_GCREGS_SETJMP @@ -93,13 +113,6 @@ #endif #endif -#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) - -extern const struct _mp_obj_module_t mp_module_os; - -#define MICROPY_PORT_BUILTIN_MODULES \ - { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&mp_module_os }, \ - // type definitions for the specific machine #ifdef __LP64__ @@ -124,9 +137,6 @@ typedef long mp_off_t; typedef void *machine_ptr_t; // must be of pointer size typedef const void *machine_const_ptr_t; // must be of pointer size -#define MICROPY_PORT_ROOT_POINTERS \ - mp_obj_t keyboard_interrupt_obj; - // We need to provide a declaration/definition of alloca() #ifdef __FreeBSD__ #include <stdlib.h> diff --git a/unix/mpthreadport.c b/unix/mpthreadport.c new file mode 100644 index 0000000000..e5cfe7a669 --- /dev/null +++ b/unix/mpthreadport.c @@ -0,0 +1,223 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include "py/mpstate.h" +#include "py/mpthread.h" +#include "py/gc.h" + +#if MICROPY_PY_THREAD + +#include <signal.h> +#include <sched.h> + +// this structure forms a linked list, one node per active thread +typedef struct _thread_t { + pthread_t id; // system id of thread + int ready; // whether the thread is ready and running + void *arg; // thread Python args, a GC root pointer + struct _thread_t *next; +} thread_t; + +STATIC pthread_key_t tls_key; + +// the mutex controls access to the linked list +STATIC pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; +STATIC thread_t *thread; + +// this is used to synchronise the signal handler of the thread +// it's needed because we can't use any pthread calls in a signal handler +STATIC volatile int thread_signal_done; + +// this signal handler is used to scan the regs and stack of a thread +STATIC void mp_thread_gc(int signo, siginfo_t *info, void *context) { + (void)info; // unused + (void)context; // unused + if (signo == SIGUSR1) { + void gc_collect_regs_and_stack(void); + gc_collect_regs_and_stack(); + // We have access to the context (regs, stack) of the thread but it seems + // that we don't need the extra information, enough is captured by the + // gc_collect_regs_and_stack function above + //gc_collect_root((void**)context, sizeof(ucontext_t) / sizeof(uintptr_t)); + thread_signal_done = 1; + } +} + +void mp_thread_init(void) { + pthread_key_create(&tls_key, NULL); + pthread_setspecific(tls_key, &mp_state_ctx.thread); + + // create first entry in linked list of all threads + thread = malloc(sizeof(thread_t)); + thread->id = pthread_self(); + thread->ready = 1; + thread->arg = NULL; + thread->next = NULL; + + // enable signal handler for garbage collection + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = mp_thread_gc; + sigemptyset(&sa.sa_mask); + sigaction(SIGUSR1, &sa, NULL); +} + +// This function scans all pointers that are external to the current thread. +// It does this by signalling all other threads and getting them to scan their +// own registers and stack. Note that there may still be some edge cases left +// with race conditions and root-pointer scanning: a given thread may manipulate +// the global root pointers (in mp_state_ctx) while another thread is doing a +// garbage collection and tracing these pointers. +void mp_thread_gc_others(void) { + pthread_mutex_lock(&thread_mutex); + for (thread_t *th = thread; th != NULL; th = th->next) { + gc_collect_root(&th->arg, 1); + if (th->id == pthread_self()) { + continue; + } + if (!th->ready) { + continue; + } + thread_signal_done = 0; + pthread_kill(th->id, SIGUSR1); + while (thread_signal_done == 0) { + sched_yield(); + } + } + pthread_mutex_unlock(&thread_mutex); +} + +mp_state_thread_t *mp_thread_get_state(void) { + return (mp_state_thread_t*)pthread_getspecific(tls_key); +} + +void mp_thread_set_state(void *state) { + pthread_setspecific(tls_key, state); +} + +void mp_thread_start(void) { + pthread_mutex_lock(&thread_mutex); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == pthread_self()) { + th->ready = 1; + break; + } + } + pthread_mutex_unlock(&thread_mutex); +} + +void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) { + // default stack size is 8k machine-words, minimum is 2k + if (*stack_size == 0) { + *stack_size = 8192 * BYTES_PER_WORD; + } else if (*stack_size < 2048 * BYTES_PER_WORD) { + *stack_size = 2048 * BYTES_PER_WORD; + } + + // set thread attributes + pthread_attr_t attr; + int ret = pthread_attr_init(&attr); + if (ret != 0) { + goto er; + } + ret = pthread_attr_setstacksize(&attr, *stack_size); + if (ret != 0) { + goto er; + } + + pthread_mutex_lock(&thread_mutex); + + // create thread + pthread_t id; + ret = pthread_create(&id, &attr, entry, arg); + if (ret != 0) { + pthread_mutex_unlock(&thread_mutex); + goto er; + } + + // adjust stack_size to provide room to recover from hitting the limit + *stack_size -= 1024 * BYTES_PER_WORD; + + // add thread to linked list of all threads + thread_t *th = malloc(sizeof(thread_t)); + th->id = id; + th->ready = 0; + th->arg = arg; + th->next = thread; + thread = th; + + pthread_mutex_unlock(&thread_mutex); + + return; + +er: + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ret))); +} + +void mp_thread_finish(void) { + pthread_mutex_lock(&thread_mutex); + // TODO unlink from list + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == pthread_self()) { + th->ready = 0; + break; + } + } + pthread_mutex_unlock(&thread_mutex); +} + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex) { + pthread_mutex_init(mutex, NULL); +} + +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { + int ret; + if (wait) { + ret = pthread_mutex_lock(mutex); + if (ret == 0) { + return 1; + } + } else { + ret = pthread_mutex_trylock(mutex); + if (ret == 0) { + return 1; + } else if (ret == EBUSY) { + return 0; + } + } + return -ret; +} + +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { + pthread_mutex_unlock(mutex); + // TODO check return value +} + +#endif // MICROPY_PY_THREAD diff --git a/unix/mpthreadport.h b/unix/mpthreadport.h new file mode 100644 index 0000000000..51cf8d7860 --- /dev/null +++ b/unix/mpthreadport.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __MICROPY_INCLUDED_UNIX_MPTHREADPORT_H__ +#define __MICROPY_INCLUDED_UNIX_MPTHREADPORT_H__ + +#include <pthread.h> + +typedef pthread_mutex_t mp_thread_mutex_t; + +void mp_thread_init(void); +void mp_thread_gc_others(void); + +#endif // __MICROPY_INCLUDED_UNIX_MPTHREADPORT_H__ |