Skip to content

Commit

Permalink
First draft with base C working
Browse files Browse the repository at this point in the history
  • Loading branch information
mcfriend99 committed Oct 14, 2024
1 parent e349b79 commit 064c303
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 31 deletions.
124 changes: 124 additions & 0 deletions libs/thread.b
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* @module threads
*
* @copyright 2024, Ore Richard Muyiwa and Blade contributors
*/

import _thread

import reflect

var _MIN_STACK_SIZE = 8 * 1024
var _DEFAULT_STACK_SIZE = 64 * 1024

class Thread {

var _fn
var _fn_arity

var _args
var _name = 'blade'
var _size = _DEFAULT_STACK_SIZE

var _ptr

Thread(fn) {
if !is_function(fn)
raise Exception('function(1..) expected, ${typeof(fn)} given')

self._fn = fn
self._fn_arity = reflect.get_function_metadata(fn).arity
}

start(...) {
self.start_from_list(__args__)
}

start_from_list(args) {
if !is_list(args)
raise Exception('list expected, ${typeof(args)} given')

self._args = args

# insert thread itself as the first argument
args = [self] + args

# only pass on arguments that the run function is able to accept.
args = args[0,self._fn_arity]

self._ptr = _thread.start(
self._fn,
args,
self._size
)
}

dispose() {
if self._ptr {
_thread.dispose(self._ptr)
}
}

await() {
if self._ptr {
_thread.await(self._ptr)
}
}

try_await() {}

sleep(time) {}

cancel() {}

yield() {}

set_name(name) {
if !is_string(name)
raise Exception('string expected, ${typeof(name)} given')

if name.length() < 0 or name.length() > 16 {
raise Exception('string length must be between 1 and 16')
}

self._name = name
_thread.set_name(name)
}

get_name() {
return self._name
}

set_stack_size(size) {
if !is_number(size)
raise Exception('number expected, ${typeof(size)} given')

if size < _MIN_STACK_SIZE
raise Exception('min size if 8kiB')

self._size = size
}

get_stack_size() {
return self._size
}

get_id() {
return _thread.get_id()
}

is_alive() {}
}


def start(function, args) {
if args == nil args = []

# we're deliberately not checking the arguments here
# because the thread initializer and start_from_list function
# will take care of that on their own.

var thread = Thread(function)
thread.start_from_list(args)
return thread
}
13 changes: 0 additions & 13 deletions libs/threads.b

This file was deleted.

61 changes: 55 additions & 6 deletions src/standard/thread.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
#include "module.h"
#include <pthread.h>

#ifdef __linux__
#include <sys/syscall.h>
#elif defined(__FreeBSD__)
#include <sys/thr.h>
#elif defined(__NetBSD__)
#include <lwp.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#else
#include "bunistd.h"
#endif /* HAVE_UNISTD_H */

static uint64_t last_thread_vm_id = 0;

#define B_THREAD_PTR_NAME "<void *thread::thread>"
Expand Down Expand Up @@ -100,18 +113,19 @@ static void *b_thread_callback_function(void *data) {
pthread_exit(NULL);
}

DECLARE_MODULE_METHOD(thread__run) {
ENFORCE_ARG_COUNT(new, 2);
ENFORCE_ARG_TYPE(new, 0, IS_CLOSURE);
ENFORCE_ARG_TYPE(new, 1, IS_LIST);
DECLARE_MODULE_METHOD(thread__start) {
ENFORCE_ARG_COUNT(start, 3);
ENFORCE_ARG_TYPE(start, 0, IS_CLOSURE);
ENFORCE_ARG_TYPE(start, 1, IS_LIST);
ENFORCE_ARG_TYPE(start, 2, IS_NUMBER);

b_thread_handle *thread = create_thread_handle(vm, AS_CLOSURE(args[0]), AS_LIST(args[1]));
if(thread != NULL) {
push_thread(vm, thread);

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 64 * 1024); // Reduce stack size to 64KB
pthread_attr_setstacksize(&attr, (size_t)AS_NUMBER(args[2])); // Reduce stack size to 64KB

if(pthread_create(&thread->thread, &attr, b_thread_callback_function, thread) == 0) {
pthread_attr_destroy(&attr);
Expand Down Expand Up @@ -161,12 +175,47 @@ DECLARE_MODULE_METHOD(thread__detach) {
RETURN_FALSE;
}

DECLARE_MODULE_METHOD(thread__set_name) {
ENFORCE_ARG_COUNT(set_name, 1);
ENFORCE_ARG_TYPE(set_name, 0, IS_STRING);
RETURN_BOOL(pthread_setname_np(AS_C_STRING(args[0])) == 0);
}

uint64_t get_thread_id(void)
{
#if defined(__linux__)
return syscall(SYS_gettid);
#elif defined(__FreeBSD__)
/* thread id is up to INT_MAX */
long tid;
thr_self(&tid);
return (uint64_t)tid;
#elif defined(__NetBSD__)
return (uint64_t)_lwp_self();
#elif defined(__OpenBSD__)
return (uint64_t)getthrid();
#elif defined(__APPLE__)
uint64_t id;
pthread_threadid_np(NULL, &id);
return id;
#else
return (uint64_t)getpid();
#endif
}

DECLARE_MODULE_METHOD(thread__get_id) {
ENFORCE_ARG_COUNT(get_id, 0);
RETURN_NUMBER(get_thread_id());
}

CREATE_MODULE_LOADER(thread) {
static b_func_reg module_functions[] = {
{"run", false, GET_MODULE_METHOD(thread__run)},
{"start", false, GET_MODULE_METHOD(thread__start)},
{"dispose", false, GET_MODULE_METHOD(thread__dispose)},
{"await", false, GET_MODULE_METHOD(thread__await)},
{"detach", false, GET_MODULE_METHOD(thread__detach)},
{"set_name", false, GET_MODULE_METHOD(thread__set_name)},
{"get_id", false, GET_MODULE_METHOD(thread__get_id)},
{NULL, false, NULL},
};

Expand Down
13 changes: 11 additions & 2 deletions src/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,9 +592,16 @@ b_vm *copy_vm(b_vm *src, uint64_t id) {

// copied globals
vm->modules = src->modules;
vm->strings = src->strings;
vm->globals = src->globals;

// every thread needs to maintain their own copy of the strings
// without this, the threads will never terminate since the parent
// vm always holds the root pointer to the strings
// this will in turn lead to an infinite hang when creating
// lots of threads in succession.
init_table(&vm->strings);
table_copy(vm, &src->strings, &vm->strings);

// copied object methods
vm->methods_string = src->methods_string;
vm->methods_list = src->methods_list;
Expand Down Expand Up @@ -626,7 +633,6 @@ void free_vm(b_vm *vm) {
if(vm->id == 0) {
free_table(vm, &vm->modules);
free_table(vm, &vm->globals);
free_table(vm, &vm->strings);

free_table(vm, &vm->methods_string);
free_table(vm, &vm->methods_list);
Expand All @@ -636,6 +642,9 @@ void free_vm(b_vm *vm) {
free_table(vm, &vm->methods_range);
}

// since every vm holds a unique copy.
free_table(vm, &vm->strings);

free(vm->threads);
free(vm->stack);

Expand Down
6 changes: 3 additions & 3 deletions tests/libs/threads.b
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _thread
import thread

# var th = _thread.new(@(i) { echo i }, [1])
# _thread.start(th)
Expand All @@ -22,7 +22,7 @@ start = microtime()
var thrds = []
for i in 0..4 {
thrds.append(
_thread.run(@(i){
thread.start(@(t, i){
for j in (i * 8)..((i * 8) + 8) {
fib(j)
}
Expand All @@ -31,7 +31,7 @@ for i in 0..4 {
}

for th in thrds {
_thread.await(th)
th.await()
}

var thread_time = microtime() - start
Expand Down
8 changes: 5 additions & 3 deletions tests/libs/threads2.b
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import _thread
import thread

var s = []
for i in 0..60000 {
var th = _thread.run(@(i){ echo i }, [i])
var th = thread.start(@(t, i){
echo '${t.get_id()}, ${i}'
}, [i])
if(th) {
s.append(th)
}
}

for i in 0..60000 {
_thread.await(s[i])
s[i].await()
}
8 changes: 4 additions & 4 deletions tests/libs/threads3.b
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _thread
import thread

import http

Expand All @@ -17,13 +17,13 @@ start = microtime()

var thrds = []
for i in 0..10 {
thrds.append(_thread.run(@{
thrds.append(thread.start(@{
echo http.get('https://google.com')
}, []))
}))
}

for t in thrds {
_thread.await(t)
t.await()
}

var thread_ends = microtime() - start
Expand Down

0 comments on commit 064c303

Please sign in to comment.