Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdatomic.h>#include <pthread.h>#include <time.h>#include <errno.h>#include <math.h>#include <uv.h>
#include "ant.h"#include "cfunc.h"#include "errors.h"#include "internal.h"#include "interrupt.h"
#include "gc/modules.h"#include "modules/buffer.h"#include "modules/atomics.h"#include "modules/timer.h"
typedef enum { ASYNC_WAIT_SETTLE_NONE = 0, ASYNC_WAIT_SETTLE_OK, ASYNC_WAIT_SETTLE_TIMED_OUT,} async_wait_settle_t;
typedef struct AsyncWaitEntry { ant_t *js; ant_value_t promise; ArrayBufferData *buffer; int32_t *address; uv_timer_t timer; uv_async_t async; bool timer_initialized; bool async_initialized; uint8_t pending_handles; _Atomic int settle_state; _Atomic bool settle_drain_microtasks; struct AsyncWaitEntry *next; struct AsyncWaitEntry *prev;} AsyncWaitEntry;
static WaitQueue global_wait_queue;static AsyncWaitEntry *async_waiters_head = NULL;
static pthread_once_t wait_queue_init_once = PTHREAD_ONCE_INIT;static pthread_mutex_t async_waiters_lock = PTHREAD_MUTEX_INITIALIZER;
static inline bool async_waiter_is_linked_locked(AsyncWaitEntry *entry) { return entry && (entry == async_waiters_head || entry->next || entry->prev);}
static void init_wait_queue(void) { wait_queue_init(&global_wait_queue);}
void wait_queue_init(WaitQueue *queue) { queue->head = NULL; pthread_mutex_init(&queue->lock, NULL);}
static void async_waiter_add_locked(AsyncWaitEntry *entry) { entry->next = async_waiters_head; entry->prev = NULL; if (async_waiters_head) async_waiters_head->prev = entry; async_waiters_head = entry;}
static void async_waiter_remove_locked(AsyncWaitEntry *entry) { if (entry->prev) entry->prev->next = entry->next; else async_waiters_head = entry->next; if (entry->next) entry->next->prev = entry->prev; entry->next = NULL; entry->prev = NULL;}
static void async_waiter_release_buffer(AsyncWaitEntry *entry) { if (!entry || !entry->buffer) return; free_array_buffer_data(entry->js, entry->buffer); entry->buffer = NULL;}
static void async_waiter_release_handle(AsyncWaitEntry *entry) { if (!entry) return; if (entry->pending_handles > 0) entry->pending_handles--; if (entry->pending_handles == 0) { async_waiter_release_buffer(entry); free(entry); }}
static void async_waiter_close_cb(uv_handle_t *handle) { AsyncWaitEntry *entry = handle ? handle->data : NULL; async_waiter_release_handle(entry);}
static void async_waiter_close_handles(AsyncWaitEntry *entry) { bool closed = false;
if (entry->timer_initialized && !uv_is_closing((uv_handle_t *)&entry->timer)) { uv_timer_stop(&entry->timer); uv_close((uv_handle_t *)&entry->timer, async_waiter_close_cb); entry->timer_initialized = false; closed = true; }
if (entry->async_initialized && !uv_is_closing((uv_handle_t *)&entry->async)) { uv_close((uv_handle_t *)&entry->async, async_waiter_close_cb); entry->async_initialized = false; closed = true; }
if (!closed) { async_waiter_release_buffer(entry); free(entry); }}
static void async_waiter_queue_settle(AsyncWaitEntry *entry, async_wait_settle_t state, bool drain_microtasks) { if (!entry) return; atomic_store(&entry->settle_drain_microtasks, drain_microtasks); atomic_store(&entry->settle_state, state); if (entry->async_initialized) uv_async_send(&entry->async);}
static void async_waiter_async_cb(uv_async_t *handle) { AsyncWaitEntry *entry = handle ? handle->data : NULL; if (!entry || !entry->js) return;
async_wait_settle_t state = (async_wait_settle_t)atomic_exchange(&entry->settle_state, ASYNC_WAIT_SETTLE_NONE); if (state == ASYNC_WAIT_SETTLE_NONE) return;
const char *result = state == ASYNC_WAIT_SETTLE_OK ? "ok" : "timed-out"; js_resolve_promise(entry->js, entry->promise, js_mkstr(entry->js, result, strlen(result))); if (atomic_load(&entry->settle_drain_microtasks)) js_maybe_drain_microtasks_after_async_settle(entry->js);
async_waiter_close_handles(entry);}
static void async_waiter_timeout_cb(uv_timer_t *timer) { AsyncWaitEntry *entry = timer ? timer->data : NULL; if (!entry) return;
pthread_mutex_lock(&async_waiters_lock); bool linked = async_waiter_is_linked_locked(entry); if (linked) async_waiter_remove_locked(entry); pthread_mutex_unlock(&async_waiters_lock);
if (linked) async_waiter_queue_settle(entry, ASYNC_WAIT_SETTLE_TIMED_OUT, true);}
static int async_waiter_notify(int32_t *address, int count) { int notified = 0; AsyncWaitEntry *ready = NULL;
pthread_mutex_lock(&async_waiters_lock); AsyncWaitEntry *current = async_waiters_head; while (current && (count == -1 || notified < count)) { AsyncWaitEntry *next = current->next; if (current->address == address) { async_waiter_remove_locked(current); current->next = ready; current->prev = NULL; ready = current; notified++; } current = next; } pthread_mutex_unlock(&async_waiters_lock); while (ready) { AsyncWaitEntry *next = ready->next; ready->next = NULL; async_waiter_queue_settle(ready, ASYNC_WAIT_SETTLE_OK, false); ready = next; }
return notified;}
void wait_queue_add(WaitQueue *queue, WaitQueueEntry *entry) { pthread_mutex_lock(&queue->lock); entry->next = queue->head; queue->head = entry; pthread_mutex_unlock(&queue->lock);}
void wait_queue_remove(WaitQueue *queue, WaitQueueEntry *entry) { pthread_mutex_lock(&queue->lock); WaitQueueEntry **current = &queue->head; while (*current) { if (*current == entry) { *current = entry->next; break; } current = &(*current)->next; } pthread_mutex_unlock(&queue->lock);}
int wait_queue_notify(WaitQueue *queue, int32_t *address, int count) { pthread_mutex_lock(&queue->lock); int notified = 0; WaitQueueEntry *current = queue->head; while (current && (count == -1 || notified < count)) { if (current->address == address) { pthread_mutex_lock(¤t->mutex); current->notified = 1; pthread_cond_signal(¤t->cond); pthread_mutex_unlock(¤t->mutex); notified++; } current = current->next; } pthread_mutex_unlock(&queue->lock); if (count == -1 || notified < count) notified += async_waiter_notify(address, count == -1 ? -1 : count - notified); return notified;}
void atomics_wake_isolate(ant_t *js) { pthread_once(&wait_queue_init_once, init_wait_queue); pthread_mutex_lock(&global_wait_queue.lock); for (WaitQueueEntry *entry = global_wait_queue.head; entry; entry = entry->next) { if (entry->js != js) continue; pthread_mutex_lock(&entry->mutex); pthread_cond_signal(&entry->cond); pthread_mutex_unlock(&entry->mutex); } pthread_mutex_unlock(&global_wait_queue.lock);}
void cleanup_atomics_module(ant_t *js) { if (!js) return; AsyncWaitEntry *removed = NULL;
pthread_mutex_lock(&async_waiters_lock); AsyncWaitEntry *current = async_waiters_head; while (current) { AsyncWaitEntry *next = current->next; if (current->js == js) { async_waiter_remove_locked(current); current->next = removed; current->prev = NULL; removed = current; } current = next; } pthread_mutex_unlock(&async_waiters_lock); while (removed) { AsyncWaitEntry *next = removed->next; removed->next = NULL; removed->prev = NULL; removed->js = NULL; removed->promise = js_mkundef(); async_waiter_release_buffer(removed); async_waiter_close_handles(removed); removed = next; }}
static bool get_atomic_array_data(ant_t *js, ant_value_t this_val, TypedArrayData **out_data, uint8_t **out_ptr) { TypedArrayData *ta_data = buffer_get_typedarray_data(this_val); size_t len; *out_data = ta_data; return Ant_Buffer_GetViewBytes(ta_data, out_ptr, &len);}
// Atomics.add(typedArray, index, value)static ant_value_t js_atomics_add(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.add requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); int32_t old_value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); old_value = atomic_fetch_add(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); old_value = atomic_fetch_add(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); old_value = atomic_fetch_add(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); old_value = atomic_fetch_add(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); old_value = atomic_fetch_add(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); old_value = atomic_fetch_add(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic operations"); } return js_mknum((double)old_value);}
// Atomics.and(typedArray, index, value)static ant_value_t js_atomics_and(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.and requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); int32_t old_value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); old_value = atomic_fetch_and(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); old_value = atomic_fetch_and(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); old_value = atomic_fetch_and(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); old_value = atomic_fetch_and(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); old_value = atomic_fetch_and(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); old_value = atomic_fetch_and(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic bitwise operations"); } return js_mknum((double)old_value);}
// Atomics.compareExchange(typedArray, index, expectedValue, replacementValue)static ant_value_t js_atomics_compareExchange(ant_params_t) { if (nargs < 4) { return js_mkerr(js, "Atomics.compareExchange requires 4 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t expected = (int32_t)js_getnum(args[2]); int32_t replacement = (int32_t)js_getnum(args[3]); switch (ta_data->type) { case TYPED_ARRAY_INT8: { int8_t exp_i8 = (int8_t)expected; _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); atomic_compare_exchange_strong(atomic_ptr, &exp_i8, (int8_t)replacement); expected = (int32_t)exp_i8; break; } case TYPED_ARRAY_UINT8: { uint8_t exp_u8 = (uint8_t)expected; _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); atomic_compare_exchange_strong(atomic_ptr, &exp_u8, (uint8_t)replacement); expected = (int32_t)exp_u8; break; } case TYPED_ARRAY_INT16: { int16_t exp_i16 = (int16_t)expected; _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); atomic_compare_exchange_strong(atomic_ptr, &exp_i16, (int16_t)replacement); expected = (int32_t)exp_i16; break; } case TYPED_ARRAY_UINT16: { uint16_t exp_u16 = (uint16_t)expected; _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); atomic_compare_exchange_strong(atomic_ptr, &exp_u16, (uint16_t)replacement); expected = (int32_t)exp_u16; break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); atomic_compare_exchange_strong(atomic_ptr, &expected, replacement); break; } case TYPED_ARRAY_UINT32: { uint32_t exp_u32 = (uint32_t)expected; _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); atomic_compare_exchange_strong(atomic_ptr, &exp_u32, (uint32_t)replacement); expected = (int32_t)exp_u32; break; } default: return js_mkerr(js, "TypedArray type not supported for atomic operations"); } return js_mknum((double)expected);}
// Atomics.exchange(typedArray, index, value)static ant_value_t js_atomics_exchange(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.exchange requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); int32_t old_value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); old_value = atomic_exchange(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); old_value = atomic_exchange(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); old_value = atomic_exchange(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); old_value = atomic_exchange(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); old_value = atomic_exchange(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); old_value = atomic_exchange(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic operations"); } return js_mknum((double)old_value);}
// Atomics.isLockFree(size)static ant_value_t js_atomics_isLockFree(ant_params_t) { if (nargs < 1) { return js_mkerr(js, "Atomics.isLockFree requires 1 argument"); } int size = (int)js_getnum(args[0]); bool is_lock_free = false; switch (size) { case 1: is_lock_free = ATOMIC_CHAR_LOCK_FREE == 2; break; case 2: is_lock_free = ATOMIC_SHORT_LOCK_FREE == 2; break; case 4: is_lock_free = ATOMIC_INT_LOCK_FREE == 2; break; case 8: is_lock_free = ATOMIC_LLONG_LOCK_FREE == 2; break; default: is_lock_free = false; } return js_bool(is_lock_free);}
// Atomics.load(typedArray, index)static ant_value_t js_atomics_load(ant_params_t) { if (nargs < 2) { return js_mkerr(js, "Atomics.load requires 2 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); value = atomic_load(atomic_ptr); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); value = atomic_load(atomic_ptr); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); value = atomic_load(atomic_ptr); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); value = atomic_load(atomic_ptr); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); value = atomic_load(atomic_ptr); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); value = atomic_load(atomic_ptr); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic operations"); } return js_mknum((double)value);}
// Atomics.or(typedArray, index, value)static ant_value_t js_atomics_or(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.or requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); int32_t old_value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); old_value = atomic_fetch_or(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); old_value = atomic_fetch_or(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); old_value = atomic_fetch_or(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); old_value = atomic_fetch_or(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); old_value = atomic_fetch_or(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); old_value = atomic_fetch_or(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic bitwise operations"); } return js_mknum((double)old_value);}
// Atomics.store(typedArray, index, value)static ant_value_t js_atomics_store(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.store requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); atomic_store(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); atomic_store(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); atomic_store(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); atomic_store(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); atomic_store(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); atomic_store(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic operations"); } return js_mknum((double)value);}
// Atomics.sub(typedArray, index, value)static ant_value_t js_atomics_sub(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.sub requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); int32_t old_value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); old_value = atomic_fetch_sub(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); old_value = atomic_fetch_sub(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); old_value = atomic_fetch_sub(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); old_value = atomic_fetch_sub(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); old_value = atomic_fetch_sub(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); old_value = atomic_fetch_sub(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic operations"); } return js_mknum((double)old_value);}
// Atomics.xor(typedArray, index, value)static ant_value_t js_atomics_xor(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.xor requires 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t value = (int32_t)js_getnum(args[2]); int32_t old_value; switch (ta_data->type) { case TYPED_ARRAY_INT8: { _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); old_value = atomic_fetch_xor(atomic_ptr, (int8_t)value); break; } case TYPED_ARRAY_UINT8: { _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); old_value = atomic_fetch_xor(atomic_ptr, (uint8_t)value); break; } case TYPED_ARRAY_INT16: { _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); old_value = atomic_fetch_xor(atomic_ptr, (int16_t)value); break; } case TYPED_ARRAY_UINT16: { _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); old_value = atomic_fetch_xor(atomic_ptr, (uint16_t)value); break; } case TYPED_ARRAY_INT32: { _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); old_value = atomic_fetch_xor(atomic_ptr, value); break; } case TYPED_ARRAY_UINT32: { _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); old_value = atomic_fetch_xor(atomic_ptr, (uint32_t)value); break; } default: return js_mkerr(js, "TypedArray type not supported for atomic bitwise operations"); } return js_mknum((double)old_value);}
// Atomics.wait(typedArray, index, value, timeout)static ant_value_t js_atomics_wait(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.wait requires at least 3 arguments"); } pthread_once(&wait_queue_init_once, init_wait_queue); TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } if (ta_data->type != TYPED_ARRAY_INT32) { return js_mkerr(js, "Atomics.wait only works with Int32Array"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t expected_value = (int32_t)js_getnum(args[2]); int64_t timeout_ms = -1; if (nargs > 3 && vtype(args[3]) == kTypeNumber) { timeout_ms = (int64_t)js_getnum(args[3]); } _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); WaitQueueEntry entry; pthread_cond_init(&entry.cond, NULL); pthread_mutex_init(&entry.mutex, NULL); entry.js = js; entry.address = (int32_t *)atomic_ptr; entry.notified = 0; pthread_mutex_lock(&global_wait_queue.lock); if (atomic_load(atomic_ptr) != expected_value) { pthread_mutex_unlock(&global_wait_queue.lock); pthread_cond_destroy(&entry.cond); pthread_mutex_destroy(&entry.mutex); return js_mkstr(js, "not-equal", 9); } entry.next = global_wait_queue.head; global_wait_queue.head = &entry; pthread_mutex_lock(&entry.mutex); pthread_mutex_unlock(&global_wait_queue.lock); const char *result = "ok"; if (timeout_ms < 0) { while (!entry.notified && !js_interrupt_pending(js)) pthread_cond_wait(&entry.cond, &entry.mutex); } else { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout_ms / 1000; ts.tv_nsec += (timeout_ms % 1000) * 1000000; if (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } while (!entry.notified && !js_interrupt_pending(js)) if (pthread_cond_timedwait(&entry.cond, &entry.mutex, &ts) == ETIMEDOUT) break; if (!entry.notified) result = "timed-out"; } bool interrupted = !entry.notified && js_interrupt_pending(js); pthread_mutex_unlock(&entry.mutex); wait_queue_remove(&global_wait_queue, &entry); pthread_cond_destroy(&entry.cond); pthread_mutex_destroy(&entry.mutex); if (interrupted) return js_interrupt_raise(js); return js_mkstr(js, result, strlen(result));}
// Atomics.notify(typedArray, index, count)static ant_value_t js_atomics_notify(ant_params_t) { if (nargs < 2) { return js_mkerr(js, "Atomics.notify requires at least 2 arguments"); } pthread_once(&wait_queue_init_once, init_wait_queue); TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } if (ta_data->type != TYPED_ARRAY_INT32) { return js_mkerr(js, "Atomics.notify only works with Int32Array"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int count = -1; if (nargs > 2 && vtype(args[2]) == kTypeNumber) { count = (int)js_getnum(args[2]); } int32_t *address = (int32_t *)(ptr + index * 4); int notified = wait_queue_notify(&global_wait_queue, address, count); return js_mknum((double)notified);}
// Atomics.waitAsync(typedArray, index, value, timeout)static ant_value_t js_atomics_waitAsync(ant_params_t) { if (nargs < 3) { return js_mkerr(js, "Atomics.waitAsync requires at least 3 arguments"); } TypedArrayData *ta_data; uint8_t *ptr; if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { return js_mkerr(js, "First argument must be a TypedArray"); } if (ta_data->type != TYPED_ARRAY_INT32) { return js_mkerr(js, "Atomics.waitAsync only works with Int32Array"); } size_t index = (size_t)js_getnum(args[1]); if (index >= ta_data->length) { return js_mkerr(js, "Index out of bounds"); } int32_t expected_value = (int32_t)js_getnum(args[2]); _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); int32_t current_value = atomic_load(atomic_ptr); double timeout_ms = HUGE_VAL;
if (nargs > 3 && vtype(args[3]) == kTypeNumber) { timeout_ms = js_getnum(args[3]); if (isnan(timeout_ms)) timeout_ms = HUGE_VAL; else if (timeout_ms < 0) timeout_ms = 0; } ant_value_t result_obj = Ant_Object_CreateBare(js); if (current_value != expected_value) { js_set(js, result_obj, "async", js_false); js_set(js, result_obj, "value", js_mkstr(js, "not-equal", 9)); return result_obj; } if (timeout_ms == 0) { js_set(js, result_obj, "async", js_false); js_set(js, result_obj, "value", js_mkstr(js, "timed-out", 9)); return result_obj; }
ant_value_t promise = js_mkpromise(js); AsyncWaitEntry *entry = calloc(1, sizeof(*entry)); if (!entry) return js_mkerr(js, "Out of memory");
entry->js = js; entry->promise = promise; entry->buffer = ta_data->buffer; entry->buffer->ref_count++; entry->address = (int32_t *)atomic_ptr; atomic_store(&entry->settle_state, ASYNC_WAIT_SETTLE_NONE); atomic_store(&entry->settle_drain_microtasks, false);
if (uv_async_init(js->loop, &entry->async, async_waiter_async_cb) != 0) { async_waiter_close_handles(entry); return js_mkerr(js, "Failed to initialize Atomics.waitAsync notifier"); } entry->async_initialized = true; entry->async.data = entry; entry->pending_handles++;
if (isfinite(timeout_ms)) { uint64_t delay = timeout_ms > (double)UINT64_MAX ? UINT64_MAX : (uint64_t)timeout_ms; if (uv_timer_init(js->loop, &entry->timer) != 0) { async_waiter_close_handles(entry); return js_mkerr(js, "Failed to initialize Atomics.waitAsync timer"); } entry->timer_initialized = true; entry->timer.data = entry; entry->pending_handles++; if (uv_timer_start(&entry->timer, async_waiter_timeout_cb, delay, 0) != 0) { async_waiter_close_handles(entry); return js_mkerr(js, "Failed to start Atomics.waitAsync timer"); } }
pthread_mutex_lock(&async_waiters_lock); bool changed = atomic_load(atomic_ptr) != expected_value; if (!changed) async_waiter_add_locked(entry); pthread_mutex_unlock(&async_waiters_lock); if (changed) async_waiter_queue_settle(entry, ASYNC_WAIT_SETTLE_OK, false);
js_set(js, result_obj, "async", js_true); js_set(js, result_obj, "value", promise);
return result_obj;}
// Atomics.pause()static ant_value_t js_atomics_pause(ant_params_t) {#if defined(__x86_64__) || defined(__i386__) __builtin_ia32_pause();#elif defined(__aarch64__) || defined(__arm__) __asm__ __volatile__("yield");#endif return js_mkundef();}
void init_atomics_module(ant_t *js) { ant_value_t atomics = Ant_Object_Create(js); defmethod(js, atomics, "add", 3, Ant_Function_CreateWithArity(js_atomics_add, 3)); defmethod(js, atomics, "and", 3, Ant_Function_CreateWithArity(js_atomics_and, 3)); defmethod(js, atomics, "compareExchange", 15, Ant_Function_CreateWithArity(js_atomics_compareExchange, 4)); defmethod(js, atomics, "exchange", 8, Ant_Function_CreateWithArity(js_atomics_exchange, 3)); defmethod(js, atomics, "isLockFree", 10, Ant_Function_CreateWithArity(js_atomics_isLockFree, 1)); defmethod(js, atomics, "load", 4, Ant_Function_CreateWithArity(js_atomics_load, 2)); defmethod(js, atomics, "notify", 6, Ant_Function_CreateWithArity(js_atomics_notify, 3)); defmethod(js, atomics, "or", 2, Ant_Function_CreateWithArity(js_atomics_or, 3)); defmethod(js, atomics, "pause", 5, Ant_Function_Create(js_atomics_pause)); defmethod(js, atomics, "store", 5, Ant_Function_CreateWithArity(js_atomics_store, 3)); defmethod(js, atomics, "sub", 3, Ant_Function_CreateWithArity(js_atomics_sub, 3)); defmethod(js, atomics, "wait", 4, Ant_Function_CreateWithArity(js_atomics_wait, 4)); defmethod(js, atomics, "waitAsync", 9, Ant_Function_CreateWithArity(js_atomics_waitAsync, 4)); defmethod(js, atomics, "xor", 3, Ant_Function_CreateWithArity(js_atomics_xor, 3)); js_set_tostringtag(js, atomics, js_mkstr(js, "Atomics", 7)); js_set_global_builtin(js, "Atomics", atomics);}
void gc_mark_atomics(ant_t *js, gc_mark_fn mark) { pthread_mutex_lock(&async_waiters_lock); for (AsyncWaitEntry *entry = async_waiters_head; entry; entry = entry->next) { if (entry->js == js) mark(js, entry->promise); } pthread_mutex_unlock(&async_waiters_lock);}