2016-10-28 14:00:55 +03:00
|
|
|
#ifndef __CR_COMMON_LOCK_H__
|
|
|
|
#define __CR_COMMON_LOCK_H__
|
2011-12-26 20:33:09 +04:00
|
|
|
|
2016-10-24 14:58:01 +03:00
|
|
|
#include <stdint.h>
|
2011-12-26 20:33:09 +04:00
|
|
|
#include <linux/futex.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <errno.h>
|
2016-10-28 14:00:55 +03:00
|
|
|
#include "common/asm/atomic.h"
|
2017-02-01 17:27:00 +03:00
|
|
|
#include "common/compiler.h"
|
2016-10-24 14:58:03 +03:00
|
|
|
|
2022-03-30 18:45:16 -07:00
|
|
|
/* scan-build complains about dereferencing a NULL pointer here. */
|
2020-10-13 06:17:32 +00:00
|
|
|
#ifndef __clang_analyzer__
|
2021-07-19 07:39:51 +00:00
|
|
|
#define LOCK_BUG_ON(condition) \
|
|
|
|
if ((condition)) \
|
|
|
|
*(volatile unsigned long *)NULL = 0xdead0000 + __LINE__
|
|
|
|
#define LOCK_BUG() LOCK_BUG_ON(1)
|
2020-10-13 06:17:32 +00:00
|
|
|
#endif /* __clang_analyzer__ */
|
2011-12-26 20:33:09 +04:00
|
|
|
|
2016-02-03 17:13:48 +01:00
|
|
|
#ifdef CR_NOGLIBC
|
2021-07-19 07:39:51 +00:00
|
|
|
#include <compel/plugins/std/syscall.h>
|
2016-02-03 17:13:48 +01:00
|
|
|
#else
|
2021-07-19 07:39:51 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
static inline long sys_futex(uint32_t *addr1, int op, uint32_t val1, struct timespec *timeout, uint32_t *addr2,
|
|
|
|
uint32_t val3)
|
2016-02-03 17:13:48 +01:00
|
|
|
{
|
2021-07-19 07:39:51 +00:00
|
|
|
int rc = syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
|
|
|
|
if (rc == -1)
|
|
|
|
rc = -errno;
|
|
|
|
return rc;
|
2016-02-03 17:13:48 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
typedef struct {
|
2012-09-11 17:55:15 +04:00
|
|
|
atomic_t raw;
|
2017-02-01 17:27:00 +03:00
|
|
|
} __aligned(sizeof(int)) futex_t;
|
2012-03-26 23:11:00 +04:00
|
|
|
|
2021-07-19 07:39:51 +00:00
|
|
|
#define FUTEX_ABORT_FLAG (0x80000000)
|
|
|
|
#define FUTEX_ABORT_RAW (-1U)
|
2012-04-03 00:52:00 +04:00
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Get current futex @f value */
|
2016-10-24 14:58:01 +03:00
|
|
|
static inline uint32_t futex_get(futex_t *f)
|
2012-01-02 20:29:02 +04:00
|
|
|
{
|
2013-08-16 19:31:57 +04:00
|
|
|
return atomic_read(&f->raw);
|
2012-01-02 20:29:02 +04:00
|
|
|
}
|
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Set futex @f value to @v */
|
2016-10-24 14:58:01 +03:00
|
|
|
static inline void futex_set(futex_t *f, uint32_t v)
|
2011-12-26 20:33:09 +04:00
|
|
|
{
|
2013-08-16 19:31:55 +04:00
|
|
|
atomic_set(&f->raw, (int)v);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
|
2021-07-19 07:39:51 +00:00
|
|
|
#define futex_init(f) futex_set(f, 0)
|
2012-03-26 23:11:00 +04:00
|
|
|
|
|
|
|
/* Wait on futex @__f value @__v become in condition @__c */
|
2021-07-19 07:39:51 +00:00
|
|
|
#define futex_wait_if_cond(__f, __v, __cond) \
|
|
|
|
do { \
|
|
|
|
int ret; \
|
|
|
|
uint32_t tmp; \
|
|
|
|
\
|
|
|
|
while (1) { \
|
|
|
|
struct timespec to = { .tv_sec = 120 }; \
|
|
|
|
tmp = futex_get(__f); \
|
|
|
|
if ((tmp & FUTEX_ABORT_FLAG) || (tmp __cond(__v))) \
|
|
|
|
break; \
|
|
|
|
ret = sys_futex((uint32_t *)&(__f)->raw.counter, FUTEX_WAIT, tmp, &to, NULL, 0); \
|
|
|
|
if (ret == -ETIMEDOUT) \
|
|
|
|
continue; \
|
|
|
|
if (ret == -EINTR || ret == -EWOULDBLOCK) \
|
|
|
|
continue; \
|
|
|
|
if (ret < 0) \
|
|
|
|
LOCK_BUG(); \
|
|
|
|
} \
|
2012-03-26 23:11:00 +04:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/* Set futex @f to @v and wake up all waiters */
|
2016-10-24 14:58:01 +03:00
|
|
|
static inline void futex_set_and_wake(futex_t *f, uint32_t v)
|
2011-12-26 20:33:09 +04:00
|
|
|
{
|
2013-08-16 19:31:55 +04:00
|
|
|
atomic_set(&f->raw, (int)v);
|
2016-10-24 14:58:03 +03:00
|
|
|
LOCK_BUG_ON(sys_futex((uint32_t *)&f->raw.counter, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) < 0);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
|
files: Implement {set,clear,wait}_fds_event()
The idea is symilar to kernel's wake_up() and wait_event().
One task needs some event. It checks the event has not
happened yet (fle hasn't received, unix peer hasn't bound, etc)
and calls get_fds_event(). Other task makes the event
(sends a fle, binds the peer to a name, etc) and calls set_fds_event().
So, while there is no an event, the first task is sleeping,
and the second wakes it up later:
Task A: clear_fds_event();
if (!socket_bound)
wait_fds_event(); /* sleep */
Task B: bind_socket();
set_fds_event(); /* wake up */
For the details of using see next patches.
v5: Use bit operations.
Split clear_fds_event from wait function.
v2: Do not wait for foreign transport sock is ready,
as it's guarantied by we create it before CR_STATE_FORKING.
travis-ci: success for Rework file opening scheme to make it asynchronous (rev5)
Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
2016-12-26 17:27:08 +03:00
|
|
|
/* Wake up all futex @f waiters */
|
|
|
|
static inline void futex_wake(futex_t *f)
|
|
|
|
{
|
|
|
|
LOCK_BUG_ON(sys_futex((uint32_t *)&f->raw.counter, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) < 0);
|
|
|
|
}
|
|
|
|
|
2012-04-03 00:52:00 +04:00
|
|
|
/* Mark futex @f as wait abort needed and wake up all waiters */
|
|
|
|
static inline void futex_abort_and_wake(futex_t *f)
|
|
|
|
{
|
|
|
|
BUILD_BUG_ON(!(FUTEX_ABORT_RAW & FUTEX_ABORT_FLAG));
|
|
|
|
futex_set_and_wake(f, FUTEX_ABORT_RAW);
|
|
|
|
}
|
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Decrement futex @f value and wake up all waiters */
|
|
|
|
static inline void futex_dec_and_wake(futex_t *f)
|
2011-12-26 20:33:09 +04:00
|
|
|
{
|
2012-03-26 23:11:00 +04:00
|
|
|
atomic_dec(&f->raw);
|
2016-10-24 14:58:03 +03:00
|
|
|
LOCK_BUG_ON(sys_futex((uint32_t *)&f->raw.counter, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) < 0);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
|
2013-01-11 14:50:44 +04:00
|
|
|
/* Increment futex @f value and wake up all waiters */
|
|
|
|
static inline void futex_inc_and_wake(futex_t *f)
|
|
|
|
{
|
|
|
|
atomic_inc(&f->raw);
|
2016-10-24 14:58:03 +03:00
|
|
|
LOCK_BUG_ON(sys_futex((uint32_t *)&f->raw.counter, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) < 0);
|
2013-01-11 14:50:44 +04:00
|
|
|
}
|
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Plain increment futex @f value */
|
2021-07-19 07:39:51 +00:00
|
|
|
static inline void futex_inc(futex_t *f)
|
|
|
|
{
|
|
|
|
atomic_inc(&f->raw);
|
|
|
|
}
|
2012-01-19 01:33:15 +03:00
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Plain decrement futex @f value */
|
2021-07-19 07:39:51 +00:00
|
|
|
static inline void futex_dec(futex_t *f)
|
|
|
|
{
|
|
|
|
atomic_dec(&f->raw);
|
|
|
|
}
|
2012-01-19 01:33:15 +03:00
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Wait until futex @f value become @v */
|
2015-11-12 19:09:00 +03:00
|
|
|
#define futex_wait_until(f, v) futex_wait_if_cond(f, v, ==)
|
2012-01-19 01:33:15 +03:00
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Wait while futex @f value is greater than @v */
|
2015-11-12 19:09:00 +03:00
|
|
|
#define futex_wait_while_gt(f, v) futex_wait_if_cond(f, v, <=)
|
2011-12-26 20:33:09 +04:00
|
|
|
|
2013-01-11 14:50:44 +04:00
|
|
|
/* Wait while futex @f value is less than @v */
|
2015-11-12 19:09:00 +03:00
|
|
|
#define futex_wait_while_lt(f, v) futex_wait_if_cond(f, v, >=)
|
2013-01-11 14:50:44 +04:00
|
|
|
|
2013-05-24 16:20:07 +04:00
|
|
|
/* Wait while futex @f value is equal to @v */
|
2015-11-12 19:09:00 +03:00
|
|
|
#define futex_wait_while_eq(f, v) futex_wait_if_cond(f, v, !=)
|
2013-05-24 16:20:07 +04:00
|
|
|
|
2012-03-26 23:11:00 +04:00
|
|
|
/* Wait while futex @f value is @v */
|
2016-10-24 14:58:01 +03:00
|
|
|
static inline void futex_wait_while(futex_t *f, uint32_t v)
|
2012-03-26 23:11:00 +04:00
|
|
|
{
|
2016-10-24 14:58:01 +03:00
|
|
|
while ((uint32_t)atomic_read(&f->raw) == v) {
|
|
|
|
int ret = sys_futex((uint32_t *)&f->raw.counter, FUTEX_WAIT, v, NULL, NULL, 0);
|
2016-10-24 14:58:03 +03:00
|
|
|
LOCK_BUG_ON(ret < 0 && ret != -EWOULDBLOCK);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-26 19:38:00 +04:00
|
|
|
typedef struct {
|
2021-07-19 07:39:51 +00:00
|
|
|
atomic_t raw;
|
2012-03-26 19:38:00 +04:00
|
|
|
} mutex_t;
|
|
|
|
|
2013-02-18 17:54:47 +04:00
|
|
|
static inline void mutex_init(mutex_t *m)
|
2011-12-26 20:33:09 +04:00
|
|
|
{
|
2016-10-24 14:58:01 +03:00
|
|
|
uint32_t c = 0;
|
2013-08-16 19:31:55 +04:00
|
|
|
atomic_set(&m->raw, (int)c);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
|
2013-02-18 17:54:47 +04:00
|
|
|
static inline void mutex_lock(mutex_t *m)
|
2011-12-26 20:33:09 +04:00
|
|
|
{
|
2016-10-24 14:58:01 +03:00
|
|
|
uint32_t c;
|
2011-12-26 20:33:09 +04:00
|
|
|
int ret;
|
|
|
|
|
2016-10-24 14:58:01 +03:00
|
|
|
while ((c = (uint32_t)atomic_inc_return(&m->raw)) != 1) {
|
|
|
|
ret = sys_futex((uint32_t *)&m->raw.counter, FUTEX_WAIT, c, NULL, NULL, 0);
|
2016-10-24 14:58:03 +03:00
|
|
|
LOCK_BUG_ON(ret < 0 && ret != -EWOULDBLOCK);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-18 17:54:47 +04:00
|
|
|
static inline void mutex_unlock(mutex_t *m)
|
2011-12-26 20:33:09 +04:00
|
|
|
{
|
2016-10-24 14:58:01 +03:00
|
|
|
uint32_t c = 0;
|
2013-08-16 19:31:55 +04:00
|
|
|
atomic_set(&m->raw, (int)c);
|
2016-10-24 14:58:03 +03:00
|
|
|
LOCK_BUG_ON(sys_futex((uint32_t *)&m->raw.counter, FUTEX_WAKE, 1, NULL, NULL, 0) < 0);
|
2011-12-26 20:33:09 +04:00
|
|
|
}
|
|
|
|
|
2016-10-28 14:00:55 +03:00
|
|
|
#endif /* __CR_COMMON_LOCK_H__ */
|