Skip to content

Commit

Permalink
support coroutine feature
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFreeman committed Nov 6, 2024
1 parent 72886f2 commit e63515b
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 8 deletions.
28 changes: 21 additions & 7 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -930,13 +930,6 @@ EOF
AC_DEFINE(HAVE_CARES, 1, [have c-ares])
fi

if test "$PHP_IOURING" = "yes"; then
PKG_CHECK_MODULES([URING], [liburing])
PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD)
PHP_EVAL_INCLINE($URING_CFLAGS)
AC_DEFINE(SW_USE_IOURING, 1, [have io_uring])
fi

AC_SWOOLE_CPU_AFFINITY
AC_SWOOLE_HAVE_REUSEPORT
AC_SWOOLE_HAVE_FUTEX
Expand All @@ -959,6 +952,27 @@ EOF

dnl Check should we link to librt

if test "$PHP_IOURING" = "yes" && test "$SW_OS" = "LINUX"; then
PKG_CHECK_MODULES([URING], [liburing])
PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD)
PHP_EVAL_INCLINE($URING_CFLAGS)
AC_DEFINE(SW_USE_IOURING, 1, [have io_uring])

LINUX_VERSION=`uname -r | cut -d '-' -f 1`
LINUX_MAJOR_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 1`
LINUX_MINIO_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 2`

_PKG_CONFIG(URING_VERSION, [modversion], [liburing])
IOURING_MAJOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 1`
IOURING_MINOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 2`

if test $IOURING_MAJOR_VERSION > 2 || (test $IOURING_MAJOR_VERSION = 2 && test $IOURING_MINOR_VERSION >= 6); then
if test $LINUX_MAJOR_VERSION > 6 || (test $LINUX_MAJOR_VERSION = 6 && test $LINUX_MAJOR_VERSION >= 7); then
AC_DEFINE(HAVE_IOURING_FUTEX, 1, [have io_uring futex feature])
fi
fi
fi

if test "$SW_OS" = "LINUX"; then
GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}')
AC_MSG_NOTICE([glibc version: $GLIBC_VERSION])
Expand Down
5 changes: 5 additions & 0 deletions ext-src/swoole_lock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ using swoole::SpinLock;
#ifdef HAVE_RWLOCK
using swoole::RWLock;
#endif
using swoole::CoroutineLock;

static zend_class_entry *swoole_lock_ce;
static zend_object_handlers swoole_lock_handlers;
Expand Down Expand Up @@ -127,6 +128,7 @@ void php_swoole_lock_minit(int module_number) {
#ifdef HAVE_SPINLOCK
SW_REGISTER_LONG_CONSTANT("SWOOLE_SPINLOCK", Lock::SPIN_LOCK);
#endif
SW_REGISTER_LONG_CONSTANT("SWOOLE_COROUTINELOCK", Lock::COROUTINE_LOCK);
}

static PHP_METHOD(swoole_lock, __construct) {
Expand Down Expand Up @@ -158,6 +160,9 @@ static PHP_METHOD(swoole_lock, __construct) {
case Lock::MUTEX:
lock = new Mutex(Mutex::PROCESS_SHARED);
break;
case Lock::COROUTINE_LOCK:
lock = new CoroutineLock();
break;
default:
zend_throw_exception(swoole_exception_ce, "lock type[%d] is not support", type);
RETURN_FALSE;
Expand Down
4 changes: 4 additions & 0 deletions ext-src/swoole_thread_lock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using swoole::SpinLock;
#ifdef HAVE_RWLOCK
using swoole::RWLock;
#endif
using swoole::CoroutineLock;

zend_class_entry *swoole_thread_lock_ce;
static zend_object_handlers swoole_thread_lock_handlers;
Expand All @@ -51,6 +52,9 @@ struct LockResource : public ThreadResource {
lock_ = new RWLock(0);
break;
#endif
case Lock::COROUTINE_LOCK:
lock_ = new CoroutineLock();
break;
case Lock::MUTEX:
default:
lock_ = new Mutex(0);
Expand Down
10 changes: 10 additions & 0 deletions include/swoole_iouring.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
#ifdef SW_USE_IOURING
#include <liburing.h>

#ifdef HAVE_IOURING_FUTEX
#ifndef FUTEX2_SIZE_U32
#define FUTEX2_SIZE_U32 0x02
#endif
#endif

using swoole::Coroutine;

enum swIouringFlag {
Expand Down Expand Up @@ -82,6 +88,10 @@ class Iouring {
static int rmdir(const char *pathname);
static int fsync(int fd);
static int fdatasync(int fd);
#ifdef HAVE_IOURING_FUTEX
static int wait_futex(uint32_t *futex);
static int wakeup_futex(uint32_t *futex);
#endif

static int callback(Reactor *reactor, Event *event);
};
Expand Down
20 changes: 19 additions & 1 deletion include/swoole_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Lock {
RW_LOCK = 1,
MUTEX = 3,
SPIN_LOCK = 5,
ATOMIC_LOCK = 6,
COROUTINE_LOCK = 6,
};
Type get_type() {
return type_;
Expand Down Expand Up @@ -106,6 +106,24 @@ class SpinLock : public Lock {
};
#endif

class CoroutineLock : public Lock {
private:
long cid = 0;
sw_atomic_t *value = nullptr;
void *coroutine = nullptr;

int lock_impl(bool blocking = true);

public:
CoroutineLock();
~CoroutineLock();
int lock_rd() override;
int lock() override;
int unlock() override;
int trylock_rd() override;
int trylock() override;
};

#if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__))
#define SW_USE_PTHREAD_BARRIER
#endif
Expand Down
48 changes: 48 additions & 0 deletions src/coroutine/iouring.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ enum IouringOpcode {
SW_IORING_OP_WRITE = IORING_OP_WRITE,
SW_IORING_OP_RENAMEAT = IORING_OP_RENAMEAT,
SW_IORING_OP_MKDIRAT = IORING_OP_MKDIRAT,
#ifdef HAVE_IOURING_FUTEX
SW_IORING_OP_FUTEX_WAIT = IORING_OP_FUTEX_WAIT,
SW_IORING_OP_FUTEX_WAKE = IORING_OP_FUTEX_WAKE,
#endif

SW_IORING_OP_FSTAT = 1000,
SW_IORING_OP_LSTAT = 1001,
Expand All @@ -54,6 +58,9 @@ struct IouringEvent {
const char *pathname;
const char *pathname2;
struct statx *statxbuf;
#ifdef HAVE_IOURING_FUTEX
uint32_t *futex;
#endif
};

Iouring::Iouring(Reactor *_reactor) {
Expand Down Expand Up @@ -206,6 +213,11 @@ static const char *get_opcode_name(IouringOpcode opcode) {
return "FSYNC";
case SW_IORING_OP_FDATASYNC:
return "FDATASYNC";
#ifdef HAVE_IOURING_FUTEX
case SW_IORING_OP_FUTEX_WAIT:
case SW_IORING_OP_FUTEX_WAKE:
return "FUTEX";
#endif
default:
return "unknown";
}
Expand Down Expand Up @@ -329,6 +341,26 @@ bool Iouring::dispatch(IouringEvent *event) {
sqe->fsync_flags = IORING_FSYNC_DATASYNC;
}
break;
#ifdef HAVE_IOURING_FUTEX
case SW_IORING_OP_FUTEX_WAIT:
sqe->opcode = SW_IORING_OP_FUTEX_WAIT;
sqe->fd = FUTEX2_SIZE_U32;
sqe->off = 1;
sqe->addr = (unsigned long) event->futex;
sqe->len = 0;
sqe->futex_flags = 0;
sqe->addr3 = FUTEX_BITSET_MATCH_ANY;
break;
case SW_IORING_OP_FUTEX_WAKE:
sqe->opcode = SW_IORING_OP_FUTEX_WAKE;
sqe->fd = FUTEX2_SIZE_U32;
sqe->off = 1;
sqe->addr = (unsigned long) event->futex;
sqe->len = 0;
sqe->futex_flags = 0;
sqe->addr3 = FUTEX_BITSET_MATCH_ANY;
break;
#endif
default:
abort();
return false;
Expand Down Expand Up @@ -465,6 +497,22 @@ int Iouring::stat(const char *path, struct stat *statbuf) {
return retval;
}

#ifdef HAVE_IOURING_FUTEX
int Iouring::wait_futex(uint32_t *futex) {
INIT_EVENT(SW_IORING_OP_FUTEX_WAIT);
event.futex = futex;

return execute(&event);
}

int Iouring::wakeup_futex(uint32_t *futex) {
INIT_EVENT(SW_IORING_OP_FUTEX_WAKE);
event.futex = futex;

return execute(&event);
}
#endif

int Iouring::callback(Reactor *reactor, Event *event) {
Iouring *iouring = static_cast<Iouring *>(event->socket->object);
return iouring->wakeup() ? SW_OK : SW_ERR;
Expand Down
118 changes: 118 additions & 0 deletions src/lock/coroutine_lock.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
+----------------------------------------------------------------------+
| Swoole |
+----------------------------------------------------------------------+
| This source file is subject to version 2.0 of the Apache license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.apache.org/licenses/LICENSE-2.0.html |
| If you did not receive a copy of the Apache2.0 license and are unable|
| to obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: NathanFreeman <[email protected]> |
+----------------------------------------------------------------------+
*/

#include "swoole.h"

#ifdef HAVE_IOURING_FUTEX
#include "swoole_iouring.h"
#else
#include "swoole_coroutine_system.h"
using swoole::coroutine::System;
#endif

#include "swoole_lock.h"

namespace swoole {
CoroutineLock::CoroutineLock() : Lock() {
type_ = COROUTINE_LOCK;
value = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t));
*value = 0;
}

Check warning on line 33 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L29-L33

Added lines #L29 - L33 were not covered by tests

CoroutineLock::~CoroutineLock() {
sw_mem_pool()->free((void *) value);
value = nullptr;
}

Check warning on line 38 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L35-L38

Added lines #L35 - L38 were not covered by tests

int CoroutineLock::lock() {
return lock_impl(true);

Check warning on line 41 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L40-L41

Added lines #L40 - L41 were not covered by tests
}

int CoroutineLock::trylock() {
return lock_impl(false);

Check warning on line 45 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L44-L45

Added lines #L44 - L45 were not covered by tests
}

int CoroutineLock::lock_rd() {
return lock_impl(true);

Check warning on line 49 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L48-L49

Added lines #L48 - L49 were not covered by tests
}

int CoroutineLock::trylock_rd() {
return lock_impl(false);

Check warning on line 53 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L52-L53

Added lines #L52 - L53 were not covered by tests
}

int CoroutineLock::unlock() {
Coroutine *current_coroutine = Coroutine::get_current();

Check warning on line 57 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L56-L57

Added lines #L56 - L57 were not covered by tests
if (current_coroutine == nullptr) {
swoole_warning("The coroutine lock can only be used in a coroutine environment");
return 1;

Check warning on line 60 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L59-L60

Added lines #L59 - L60 were not covered by tests
}

if (*value == 0) {
return 0;

Check warning on line 64 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L64

Added line #L64 was not covered by tests
}

*value = 0;
cid = 0;
coroutine = nullptr;

Check warning on line 69 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L67-L69

Added lines #L67 - L69 were not covered by tests
#ifdef HAVE_IOURING_FUTEX
return Iouring::wakeup_futex((uint32_t *) value) >= 0 ? 0 : 1;
#else
return 0;

Check warning on line 73 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L73

Added line #L73 was not covered by tests
#endif
}

int CoroutineLock::lock_impl(bool blocking) {
Coroutine *current_coroutine = Coroutine::get_current();

Check warning on line 78 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L77-L78

Added lines #L77 - L78 were not covered by tests
if (current_coroutine == nullptr) {
swoole_warning("The coroutine lock can only be used in a coroutine environment");
return 1;

Check warning on line 81 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L80-L81

Added lines #L80 - L81 were not covered by tests
}

if (current_coroutine == static_cast<Coroutine *>(coroutine) && current_coroutine->get_cid() == cid) {
return 0;

Check warning on line 85 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L85

Added line #L85 was not covered by tests
}

int result = 0;

Check warning on line 88 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L88

Added line #L88 was not covered by tests
#ifdef HAVE_IOURING_FUTEX
if (!sw_atomic_cmp_set(value, 0, 1)) {
if (!blocking) {
return 1;
}

result = Iouring::wait_futex((uint32_t *) value);
*value = 1;
}
#else
while (true) {
if (sw_atomic_cmp_set(value, 0, 1)) {
break;

Check warning on line 101 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L101

Added line #L101 was not covered by tests
}

if (!blocking) {
return 1;

Check warning on line 105 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L105

Added line #L105 was not covered by tests
}

if (System::sleep((double) 0.1) != 0) {
return 1;

Check warning on line 109 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L109

Added line #L109 was not covered by tests
}
}
#endif

cid = current_coroutine->get_cid();
coroutine = (void *) current_coroutine;
return result;

Check warning on line 116 in src/lock/coroutine_lock.cc

View check run for this annotation

Codecov / codecov/patch

src/lock/coroutine_lock.cc#L114-L116

Added lines #L114 - L116 were not covered by tests
}
} // namespace swoole
Loading

0 comments on commit e63515b

Please sign in to comment.