summaryrefslogtreecommitdiffstatshomepage
path: root/unix
diff options
context:
space:
mode:
Diffstat (limited to 'unix')
-rw-r--r--unix/Makefile38
-rw-r--r--unix/alloc.c23
-rw-r--r--unix/file.c12
-rw-r--r--unix/gccollect.c18
-rw-r--r--unix/main.c26
-rw-r--r--unix/modmachine.c7
-rw-r--r--unix/modsocket.c2
-rw-r--r--unix/modtime.c13
-rw-r--r--unix/mpconfigport.h21
-rw-r--r--unix/mpconfigport.mk11
-rw-r--r--unix/mpconfigport_coverage.h4
-rw-r--r--unix/mpconfigport_minimal.h30
-rw-r--r--unix/mpthreadport.c223
-rw-r--r--unix/mpthreadport.h36
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*)&regs;
- gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_VM(stack_top) - (uintptr_t)&regs) / sizeof(uintptr_t));
+ gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)&regs) / 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__