diff --git a/pkgs/unix_api/a.out b/pkgs/unix_api/a.out deleted file mode 100755 index 42fc0318..00000000 Binary files a/pkgs/unix_api/a.out and /dev/null differ diff --git a/pkgs/unix_api/constants.yaml b/pkgs/unix_api/constants.yaml index 3fba4a1d..3fbe942c 100644 --- a/pkgs/unix_api/constants.yaml +++ b/pkgs/unix_api/constants.yaml @@ -64,9 +64,11 @@ - EMFILE - ENOENT - ENOSPC + - ENOSYS - ENOTDIR - ENOTEMPTY - EPERM + - ETIMEDOUT "": - AT_FDCWD - AT_REMOVEDIR diff --git a/pkgs/unix_api/hook/build.dart b/pkgs/unix_api/hook/build.dart index a1d79d07..11c54fdc 100644 --- a/pkgs/unix_api/hook/build.dart +++ b/pkgs/unix_api/hook/build.dart @@ -16,6 +16,7 @@ void main(List args) async { assetName: 'libc_shim', libraries: [ if ([OS.linux].contains(input.config.code.targetOS)) 'crypt', + if (input.config.code.targetOS != OS.android) 'pthread', ], sources: [ 'src/libc_shim.c', diff --git a/pkgs/unix_api/lib/src/bespoke.dart b/pkgs/unix_api/lib/src/bespoke.dart index 9d792056..d7e642f9 100644 --- a/pkgs/unix_api/lib/src/bespoke.dart +++ b/pkgs/unix_api/lib/src/bespoke.dart @@ -6,7 +6,8 @@ import 'dart:ffi' as ffi; import 'errno.dart'; import 'libc_bindings.g.dart'; -export 'libc_bindings.g.dart' show DIR, dirent, Stat, timespec; +export 'libc_bindings.g.dart' + show DIR, dirent, Stat, timespec, pthread_t, pthread_attr_t; /// Gets metadata for a file. /// @@ -65,3 +66,103 @@ extension DirentPtrExtensions on ffi.Pointer { /// Need because of https://github.com/dart-lang/sdk/issues/41237. ffi.Pointer get d_name_ptr => libc_shim_d_name_ptr(this); } + +// + +/// Unlocks all threads waiting on a condition variable. +/// +/// See the [POSIX specification for `pthread_cond_broadcast`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html). +int pthread_cond_broadcast(ffi.Pointer cond) => + libc_shim_pthread_cond_broadcast(cond, errnoPtr); + +/// Destroys a condition variable. +/// +/// See the [POSIX specification for `pthread_cond_destroy`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_destroy.html). +int pthread_cond_destroy(ffi.Pointer cond) => + libc_shim_pthread_cond_destroy(cond, errnoPtr); + +/// Initializes a condition variable. +/// +/// See the [POSIX specification for `pthread_cond_init`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_init.html). +int pthread_cond_init( + ffi.Pointer cond, + ffi.Pointer attr, +) => libc_shim_pthread_cond_init(cond, attr, errnoPtr); + +/// Unlocks one thread waiting on a condition variable. +/// +/// See the [POSIX specification for `pthread_cond_signal`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html). +int pthread_cond_signal(ffi.Pointer cond) => + libc_shim_pthread_cond_signal(cond, errnoPtr); + +/// Waits on a condition variable for a given amount of time. +/// +/// See the [POSIX specification for `pthread_cond_timedwait`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html). +int pthread_cond_timedwait( + ffi.Pointer cond, + ffi.Pointer mutex, + ffi.Pointer abstime, +) => libc_shim_pthread_cond_timedwait(cond, mutex, abstime, errnoPtr); + +/// Waits on a condition variable. +/// +/// See the [POSIX specification for `pthread_cond_wait`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html). +int pthread_cond_wait( + ffi.Pointer cond, + ffi.Pointer mutex, +) => libc_shim_pthread_cond_wait(cond, mutex, errnoPtr); + +/// Creates a new thread. +/// +/// See the [POSIX specification for `pthread_create`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html). +int pthread_create( + ffi.Pointer thread, + ffi.Pointer attr, + ffi.Pointer< + ffi.NativeFunction Function(ffi.Pointer)> + > + start_routine, + ffi.Pointer arg, +) => libc_shim_pthread_create(thread, attr, start_routine, arg, errnoPtr); + +/// Detaches a thread. +/// +/// See the [POSIX specification for `pthread_detach`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html). +int pthread_detach(pthread_t thread) => + libc_shim_pthread_detach(thread, errnoPtr); + +/// Destroys a mutex. +/// +/// See the [POSIX specification for `pthread_mutex_destroy`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_destroy.html). +int pthread_mutex_destroy(ffi.Pointer mutex) => + libc_shim_pthread_mutex_destroy(mutex, errnoPtr); + +/// Initializes a mutex. +/// +/// See the [POSIX specification for `pthread_mutex_init`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). +int pthread_mutex_init( + ffi.Pointer mutex, + ffi.Pointer attr, +) => libc_shim_pthread_mutex_init(mutex, attr, errnoPtr); + +/// Locks a mutex. +/// +/// See the [POSIX specification for `pthread_mutex_lock`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html). +int pthread_mutex_lock(ffi.Pointer mutex) => + libc_shim_pthread_mutex_lock(mutex, errnoPtr); + +/// Locks a mutex, failing if the lock is not acquired before a timeout. +/// +/// Available on Android and Linux. +/// +/// See the [POSIX specification for `pthread_mutex_timedlock`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html). +int pthread_mutex_timedlock( + ffi.Pointer mutex, + ffi.Pointer abs_timeout, +) => libc_shim_pthread_mutex_timedlock(mutex, abs_timeout, errnoPtr); + +/// Unlocks a mutex. +/// +/// See the [POSIX specification for `pthread_mutex_unlock`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html). +int pthread_mutex_unlock(ffi.Pointer mutex) => + libc_shim_pthread_mutex_unlock(mutex, errnoPtr); diff --git a/pkgs/unix_api/lib/src/constant_bindings.g.dart b/pkgs/unix_api/lib/src/constant_bindings.g.dart index bbf36df9..77cba65a 100644 --- a/pkgs/unix_api/lib/src/constant_bindings.g.dart +++ b/pkgs/unix_api/lib/src/constant_bindings.g.dart @@ -58,6 +58,9 @@ external int get_ENOENT(); @ffi.Native(symbol: 'libc_shim_get_ENOSPC') external int get_ENOSPC(); +@ffi.Native(symbol: 'libc_shim_get_ENOSYS') +external int get_ENOSYS(); + @ffi.Native(symbol: 'libc_shim_get_ENOTDIR') external int get_ENOTDIR(); @@ -67,6 +70,9 @@ external int get_ENOTEMPTY(); @ffi.Native(symbol: 'libc_shim_get_EPERM') external int get_EPERM(); +@ffi.Native(symbol: 'libc_shim_get_ETIMEDOUT') +external int get_ETIMEDOUT(); + @ffi.Native(symbol: 'libc_shim_get_AT_FDCWD') external int get_AT_FDCWD(); diff --git a/pkgs/unix_api/lib/src/constants.g.dart b/pkgs/unix_api/lib/src/constants.g.dart index 9268df81..3ad7fa9e 100644 --- a/pkgs/unix_api/lib/src/constants.g.dart +++ b/pkgs/unix_api/lib/src/constants.g.dart @@ -158,6 +158,15 @@ int get ENOSPC { } } +int get ENOSYS { + final v = get_ENOSYS(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('ENOSYS'); + } else { + return v; + } +} + int get ENOTDIR { final v = get_ENOTDIR(); if (v == libc_shim_UNDEFINED) { @@ -185,6 +194,15 @@ int get EPERM { } } +int get ETIMEDOUT { + final v = get_ETIMEDOUT(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('ETIMEDOUT'); + } else { + return v; + } +} + int get AT_FDCWD { final v = get_AT_FDCWD(); if (v == libc_shim_UNDEFINED) { diff --git a/pkgs/unix_api/lib/src/libc_bindings.g.dart b/pkgs/unix_api/lib/src/libc_bindings.g.dart index c4704703..b0c8f446 100644 --- a/pkgs/unix_api/lib/src/libc_bindings.g.dart +++ b/pkgs/unix_api/lib/src/libc_bindings.g.dart @@ -37,6 +37,149 @@ external ffi.Pointer libc_shim_readdir( ffi.Pointer err, ); +@ffi.Native< + ffi.Int Function(ffi.Pointer, ffi.Pointer) +>() +external int libc_shim_pthread_cond_broadcast( + ffi.Pointer cond, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function(ffi.Pointer, ffi.Pointer) +>() +external int libc_shim_pthread_cond_destroy( + ffi.Pointer cond, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) +>() +external int libc_shim_pthread_cond_init( + ffi.Pointer cond, + ffi.Pointer attr, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function(ffi.Pointer, ffi.Pointer) +>() +external int libc_shim_pthread_cond_signal( + ffi.Pointer cond, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) +>() +external int libc_shim_pthread_cond_timedwait( + ffi.Pointer cond, + ffi.Pointer mutex, + ffi.Pointer abstime, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) +>() +external int libc_shim_pthread_cond_wait( + ffi.Pointer cond, + ffi.Pointer mutex, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction Function(ffi.Pointer)> + >, + ffi.Pointer, + ffi.Pointer, + ) +>() +external int libc_shim_pthread_create( + ffi.Pointer thread, + ffi.Pointer attr, + ffi.Pointer< + ffi.NativeFunction Function(ffi.Pointer)> + > + start_routine, + ffi.Pointer arg, + ffi.Pointer err, +); + +@ffi.Native)>() +external int libc_shim_pthread_detach( + pthread_t thread, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function(ffi.Pointer, ffi.Pointer) +>() +external int libc_shim_pthread_mutex_destroy( + ffi.Pointer mutex, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) +>() +external int libc_shim_pthread_mutex_init( + ffi.Pointer mutex, + ffi.Pointer attr, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function(ffi.Pointer, ffi.Pointer) +>() +external int libc_shim_pthread_mutex_lock( + ffi.Pointer mutex, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) +>() +external int libc_shim_pthread_mutex_timedlock( + ffi.Pointer mutex, + ffi.Pointer abs_timeout, + ffi.Pointer err, +); + +@ffi.Native< + ffi.Int Function(ffi.Pointer, ffi.Pointer) +>() +external int libc_shim_pthread_mutex_unlock( + ffi.Pointer mutex, + ffi.Pointer err, +); + @ffi.Native< ffi.Int Function( ffi.Pointer, @@ -110,6 +253,31 @@ final class DIR extends ffi.Struct { external ffi.Pointer _dir; } +/// +final class pthread_t extends ffi.Struct { + external ffi.Pointer _pthread_t; +} + +final class pthread_attr_t extends ffi.Struct { + external ffi.Pointer _pthread_attr_t; +} + +final class pthread_mutex_t extends ffi.Struct { + external ffi.Pointer _pthread_mutex_t; +} + +final class pthread_mutexattr_t extends ffi.Struct { + external ffi.Pointer _pthread_mutexattr_t; +} + +final class pthread_cond_t extends ffi.Struct { + external ffi.Pointer _pthread_cond_t; +} + +final class pthread_condattr_t extends ffi.Struct { + external ffi.Pointer _pthread_condattr_t; +} + /// final class timespec extends ffi.Struct { @ffi.Int64() diff --git a/pkgs/unix_api/src/constants.g.c b/pkgs/unix_api/src/constants.g.c index fa45bb1c..4c79c6d9 100644 --- a/pkgs/unix_api/src/constants.g.c +++ b/pkgs/unix_api/src/constants.g.c @@ -132,6 +132,13 @@ int64_t libc_shim_get_ENOSPC(void) { #endif return libc_shim_UNDEFINED; } +int64_t libc_shim_get_ENOSYS(void) { +#ifdef ENOSYS + assert(ENOSYS != libc_shim_UNDEFINED); + return ENOSYS; +#endif + return libc_shim_UNDEFINED; +} int64_t libc_shim_get_ENOTDIR(void) { #ifdef ENOTDIR assert(ENOTDIR != libc_shim_UNDEFINED); @@ -153,6 +160,13 @@ int64_t libc_shim_get_EPERM(void) { #endif return libc_shim_UNDEFINED; } +int64_t libc_shim_get_ETIMEDOUT(void) { +#ifdef ETIMEDOUT + assert(ETIMEDOUT != libc_shim_UNDEFINED); + return ETIMEDOUT; +#endif + return libc_shim_UNDEFINED; +} int64_t libc_shim_get_AT_FDCWD(void) { #ifdef AT_FDCWD assert(AT_FDCWD != libc_shim_UNDEFINED); diff --git a/pkgs/unix_api/src/constants.g.h b/pkgs/unix_api/src/constants.g.h index f1889af4..7cc9cc66 100644 --- a/pkgs/unix_api/src/constants.g.h +++ b/pkgs/unix_api/src/constants.g.h @@ -44,12 +44,16 @@ int64_t libc_shim_get_ENOENT(void); __attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_ENOSPC(void); __attribute__((visibility("default"))) __attribute__((used)) +int64_t libc_shim_get_ENOSYS(void); +__attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_ENOTDIR(void); __attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_ENOTEMPTY(void); __attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_EPERM(void); __attribute__((visibility("default"))) __attribute__((used)) +int64_t libc_shim_get_ETIMEDOUT(void); +__attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_AT_FDCWD(void); __attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_AT_REMOVEDIR(void); diff --git a/pkgs/unix_api/src/libc_shim.c b/pkgs/unix_api/src/libc_shim.c index 8c0c0c20..77508d8f 100644 --- a/pkgs/unix_api/src/libc_shim.c +++ b/pkgs/unix_api/src/libc_shim.c @@ -8,10 +8,12 @@ #include #include #include +#include #include #include #include #include +#include #include // @@ -137,8 +139,8 @@ int libc_shim_fstat(int fd, struct libc_shim_Stat *buf, int *err) { return r; } -int libc_shim_fstatat(int fd, char *path, struct libc_shim_Stat *buf, - int flag, int *err) { +int libc_shim_fstatat(int fd, char *path, struct libc_shim_Stat *buf, int flag, + int *err) { struct stat s; errno = *err; int r = fstatat(fd, path, &s, flag); @@ -148,3 +150,152 @@ int libc_shim_fstatat(int fd, char *path, struct libc_shim_Stat *buf, } return r; } + +// + +int libc_shim_pthread_cond_broadcast(libc_shim_pthread_cond_t *cond, int *err) { + errno = *err; + int r = pthread_cond_broadcast((pthread_cond_t *)cond->_pthread_cond_t); + *err = errno; + return r; +} + +int libc_shim_pthread_cond_destroy(libc_shim_pthread_cond_t *cond, int *err) { + errno = *err; + int r = pthread_cond_destroy((pthread_cond_t *)cond->_pthread_cond_t); + *err = errno; + free(cond->_pthread_cond_t); + return r; +} + +int libc_shim_pthread_cond_init(libc_shim_pthread_cond_t *cond, + const libc_shim_pthread_condattr_t *attr, + int *err) { + pthread_cond_t *c = (pthread_cond_t *)calloc(1, sizeof(pthread_cond_t)); + errno = *err; + int r = pthread_cond_init( + c, + (attr == NULL) ? NULL : (pthread_condattr_t *)attr->_pthread_condattr_t); + *err = errno; + if (r == 0) { + cond->_pthread_cond_t = c; + } else { + free(c); + } + return r; +} + +int libc_shim_pthread_cond_signal(libc_shim_pthread_cond_t *cond, int *err) { + errno = *err; + int r = pthread_cond_signal((pthread_cond_t *)cond->_pthread_cond_t); + *err = errno; + return r; +} + +int libc_shim_pthread_cond_timedwait(libc_shim_pthread_cond_t *cond, + libc_shim_pthread_mutex_t *mutex, + const struct libc_shim_timespec *abstime, + int *err) { + struct timespec s; + + s.tv_nsec = abstime->tv_nsec; + s.tv_sec = abstime->tv_sec; + + errno = *err; + int r = + pthread_cond_timedwait((pthread_cond_t *)cond->_pthread_cond_t, + (pthread_mutex_t *)mutex->_pthread_mutex_t, &s); + *err = errno; + return r; +} + +int libc_shim_pthread_cond_wait(libc_shim_pthread_cond_t *cond, + libc_shim_pthread_mutex_t *mutex, int *err) { + errno = *err; + int r = pthread_cond_wait((pthread_cond_t *)cond->_pthread_cond_t, + (pthread_mutex_t *)mutex->_pthread_mutex_t); + *err = errno; + return r; +} + +int libc_shim_pthread_create(libc_shim_pthread_t *restrict thread, + const libc_shim_pthread_attr_t *restrict attr, + void *(*start_routine)(void *), void *restrict arg, + int *err) { + pthread_t *t = (pthread_t *)calloc(1, sizeof(pthread_t)); + errno = *err; + int r = pthread_create(t, (attr == NULL) ? NULL : attr->_pthread_attr_t, + start_routine, arg); + *err = errno; + if (r == 0) { + thread->_pthread_t = t; + } else { + free(t); + } + return r; +} + +int libc_shim_pthread_detach(libc_shim_pthread_t thread, int *err) { + errno = *err; + int r = pthread_detach(*((pthread_t *)thread._pthread_t)); + *err = errno; + free(thread._pthread_t); + return r; +} + +int libc_shim_pthread_mutex_destroy(libc_shim_pthread_mutex_t *mutex, + int *err) { + errno = *err; + int r = pthread_mutex_destroy(mutex->_pthread_mutex_t); + *err = errno; + free(mutex->_pthread_mutex_t); + return r; +} + +int libc_shim_pthread_mutex_init( + libc_shim_pthread_mutex_t *restrict mutex, + const libc_shim_pthread_mutexattr_t *restrict attr, int *err) { + pthread_mutex_t *m = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t)); + errno = *err; + int r = + pthread_mutex_init(m, (attr == NULL) ? NULL : attr->_pthread_mutexattr_t); + *err = errno; + if (r == 0) { + mutex->_pthread_mutex_t = m; + } else { + free(m); + } + return r; +} + +int libc_shim_pthread_mutex_lock(libc_shim_pthread_mutex_t *mutex, int *err) { + errno = *err; + int r = pthread_mutex_lock((pthread_mutex_t *)mutex->_pthread_mutex_t); + *err = errno; + return r; +} + +int libc_shim_pthread_mutex_timedlock( + libc_shim_pthread_mutex_t *restrict mutex, + const struct libc_shim_timespec *restrict abs_timeout, int *err) { +#if defined(__linux__) || defined(__ANDROID__) + struct timespec s; + s.tv_nsec = abs_timeout->tv_nsec; + s.tv_sec = abs_timeout->tv_sec; + + errno = *err; + int r = + pthread_mutex_timedlock((pthread_mutex_t *)mutex->_pthread_mutex_t, &s); + *err = errno; + return r; +#else + return ENOSYS; +#endif +} + +int libc_shim_pthread_mutex_unlock(libc_shim_pthread_mutex_t *mutex, int *err) { + errno = *err; + int r = pthread_mutex_unlock((pthread_mutex_t *)mutex->_pthread_mutex_t); + *err = errno; + return r; +} diff --git a/pkgs/unix_api/src/libc_shim.h b/pkgs/unix_api/src/libc_shim.h index 99a81412..b06b3a9b 100644 --- a/pkgs/unix_api/src/libc_shim.h +++ b/pkgs/unix_api/src/libc_shim.h @@ -60,7 +60,84 @@ LIBC_SHIM_EXPORT char *libc_shim_d_name_ptr(struct libc_shim_dirent *d); LIBC_SHIM_EXPORT int libc_shim_closedir(libc_shim_DIR *d, int *err); LIBC_SHIM_EXPORT libc_shim_DIR *libc_shim_fdopendir(int fd, int *err); LIBC_SHIM_EXPORT libc_shim_DIR *libc_shim_opendir(const char *path, int *err); -LIBC_SHIM_EXPORT struct libc_shim_dirent *libc_shim_readdir(libc_shim_DIR *d, int *err); +LIBC_SHIM_EXPORT struct libc_shim_dirent *libc_shim_readdir(libc_shim_DIR *d, + int *err); + +// + +typedef struct { + void *_pthread_t; +} libc_shim_pthread_t; + +typedef struct { + void *_pthread_attr_t; +} libc_shim_pthread_attr_t; + +typedef struct { + void *_pthread_mutex_t; +} libc_shim_pthread_mutex_t; + +typedef struct { + void *_pthread_mutexattr_t; +} libc_shim_pthread_mutexattr_t; + +typedef struct { + void *_pthread_cond_t; +} libc_shim_pthread_cond_t; + +typedef struct { + void *_pthread_condattr_t; +} libc_shim_pthread_condattr_t; + +struct libc_shim_timespec; + +LIBC_SHIM_EXPORT int +libc_shim_pthread_cond_broadcast(libc_shim_pthread_cond_t *cond, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_cond_destroy(libc_shim_pthread_cond_t *cond, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_cond_init(libc_shim_pthread_cond_t *cond, + const libc_shim_pthread_condattr_t *attr, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_cond_signal(libc_shim_pthread_cond_t *cond, int *err); + +LIBC_SHIM_EXPORT int libc_shim_pthread_cond_timedwait( + libc_shim_pthread_cond_t *cond, libc_shim_pthread_mutex_t *mutex, + const struct libc_shim_timespec *abstime, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_cond_wait(libc_shim_pthread_cond_t *cond, + libc_shim_pthread_mutex_t *mutex, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_create(libc_shim_pthread_t *restrict thread, + const libc_shim_pthread_attr_t *restrict attr, + void *(*start_routine)(void *), void *restrict arg, + int *err); + +LIBC_SHIM_EXPORT int libc_shim_pthread_detach(libc_shim_pthread_t thread, + int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_mutex_destroy(libc_shim_pthread_mutex_t *mutex, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_mutex_init(libc_shim_pthread_mutex_t *restrict mutex, + const libc_shim_pthread_mutexattr_t *restrict attr, + int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_mutex_lock(libc_shim_pthread_mutex_t *mutex, int *err); + +LIBC_SHIM_EXPORT int libc_shim_pthread_mutex_timedlock( + libc_shim_pthread_mutex_t *restrict mutex, + const struct libc_shim_timespec *restrict abs_timeout, int *err); + +LIBC_SHIM_EXPORT int +libc_shim_pthread_mutex_unlock(libc_shim_pthread_mutex_t *mutex, int *err); // struct libc_shim_timespec { @@ -90,6 +167,8 @@ LIBC_SHIM_EXPORT int libc_shim_stat(const char *path, struct libc_shim_Stat *buf, int *err); LIBC_SHIM_EXPORT int libc_shim_lstat(const char *path, struct libc_shim_Stat *buf, int *err); -LIBC_SHIM_EXPORT int libc_shim_fstat(int fd, struct libc_shim_Stat *buf, int *err); +LIBC_SHIM_EXPORT int libc_shim_fstat(int fd, struct libc_shim_Stat *buf, + int *err); LIBC_SHIM_EXPORT int libc_shim_fstatat(int fd, char *path, - struct libc_shim_Stat *buf, int flag, int *err); + struct libc_shim_Stat *buf, int flag, + int *err); diff --git a/pkgs/unix_api/test/pthread_test.dart b/pkgs/unix_api/test/pthread_test.dart new file mode 100644 index 00000000..19417d39 --- /dev/null +++ b/pkgs/unix_api/test/pthread_test.dart @@ -0,0 +1,118 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; +import 'dart:ffi'; +import 'dart:ffi' as ffi; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:ffi/ffi.dart' as ffi; +import 'package:unix_api/src/libc_bindings.g.dart'; +import 'package:unix_api/unix_api.dart'; +import 'package:test/test.dart'; + +typedef pthread_create_callback = ffi.Void Function(ffi.Pointer); + +void main() { + group('pthread', () { + late ffi.Arena arena; + + setUp(() { + arena = ffi.Arena(); + }); + + tearDown(() { + arena.releaseAll(); + }); + + test('pthread_create/pthread_detach', () async { + final thread = arena(); + final c = Completer(); + + final threadCallback = NativeCallable.listener(( + ffi.Pointer arg, + ) async { + c.complete(); + }); + + pthread_create( + thread, + nullptr, + threadCallback.nativeFunction.cast(), + nullptr, + ); + pthread_detach(thread.ref); + + await c.future; + }); + + test('pthread_mutex_*', () async { + final mutex = arena(); + final time = arena(); + time.ref.tv_nsec = 0; + time.ref.tv_sec = 0; + + expect(pthread_mutex_init(mutex, nullptr), 0); + expect(pthread_mutex_lock(mutex), 0); + await Isolate.run(() { + assert( + pthread_mutex_timedlock(mutex, time) == + ((Platform.isAndroid || Platform.isLinux) ? ETIMEDOUT : ENOSYS), + ); + }); + + final lockTestIsolate = Isolate.run(() { + assert(pthread_mutex_lock(mutex) == 0); + }); + pthread_mutex_unlock(mutex); + await lockTestIsolate; + assert(pthread_mutex_unlock(mutex) == 0); // Locked by Isolate. + expect(pthread_mutex_destroy(mutex), 0); + }); + + test('pthread_cond_*', () async { + final cond = arena(); + final mutex = arena(); + final time = arena(); + time.ref.tv_nsec = 0; + time.ref.tv_sec = 0; + + expect(pthread_mutex_init(mutex, nullptr), 0); + expect(pthread_cond_init(cond, nullptr), 0); + expect(pthread_mutex_lock(mutex), 0); + expect(pthread_cond_timedwait(cond, mutex, time), ETIMEDOUT); + expect(pthread_mutex_unlock(mutex), 0); + + // Tests waiting on a signal. + var waitOnSignalDone = false; + Isolate.run(() { + assert(pthread_mutex_lock(mutex) == 0); + assert(pthread_cond_wait(cond, mutex) == 0); + assert(pthread_mutex_unlock(mutex) == 0); + }).whenComplete(() => waitOnSignalDone = true); + + while (!waitOnSignalDone) { + expect(pthread_cond_signal(cond), 0); + await Future.delayed(const Duration()); + } + + // Tests waiting on a signal. + var waitOnSignalBroadcast = false; + Isolate.run(() { + assert(pthread_mutex_lock(mutex) == 0); + assert(pthread_cond_wait(cond, mutex) == 0); + assert(pthread_mutex_unlock(mutex) == 0); + }).whenComplete(() => waitOnSignalBroadcast = true); + + while (!waitOnSignalBroadcast) { + expect(pthread_cond_broadcast(cond), 0); + await Future.delayed(const Duration()); + } + + expect(pthread_mutex_destroy(mutex), 0); + expect(pthread_cond_destroy(cond), 0); + }); + }); +}