summaryrefslogtreecommitdiffstats
path: root/include/libtf/fiber.h
blob: f97e9630b860d1f175d1fe4454bb1420374d2260 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* fiber.h - libtf fiber manager header
 *
 * Copyright (C) 2009-2010 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 or later as
 * published by the Free Software Foundation.
 *
 * See http://www.gnu.org/ for details.
 */

#ifndef TF_FIBER_H
#define TF_FIBER_H

#include <errno.h>
#include <libtf/defines.h>
#include <libtf/heap.h>
#include <libtf/list.h>

/* Inter-fibre calls */
struct tf_fiber;
struct tf_ifc;
typedef void (*tf_ifc_handler_t)(void *fiber, struct tf_ifc *msg);

struct tf_ifc {
	union {
		struct tf_ifc		*next;
		struct tf_list_node	list;
	};
	tf_ifc_handler_t		handler;
	struct tf_fiber *		sender;
};

void tf_ifc_queue(void *fiber, struct tf_ifc *msg, tf_ifc_handler_t handler);
void tf_ifc_complete(void *fiber, struct tf_ifc *msg, tf_ifc_handler_t handler);
void tf_ifc_process(void);
void tf_ifc_process_unordered(void);

/* Timeouts */
struct tf_timeout_manager {
	struct tf_heap_head		heap;
	unsigned int			num_fibers;
};

struct tf_timeout_client {
	struct tf_timeout_manager *	manager;
	tf_mtime_t			value;
	tf_mtime_t			latched;
	struct tf_heap_node		heap_node;
	struct tf_ifc			ifc;
};

struct tf_timeout {
	tf_mtime_t			saved_timeout;
	tf_mtime_t			my_timeout;
};

static inline int tf_timeout_expired(struct tf_timeout *to)
{
	return tf_mtime_diff(to->my_timeout, tf_mtime_now()) <= 0;
}

struct tf_timeout_fiber *tf_timeout_fiber_create(void);
void tf_timeout_adjust(struct tf_timeout_client *tc);
void tf_timeout_delete(struct tf_timeout_client *tc);

#define tf_timed(__to, func, timeout)					\
	({								\
		tf_timeout_push(__to, timeout);				\
		tf_timeout_pop(__to, (func));				\
	})

/* Fibres and their management */
typedef void *tf_uctx_t;

struct tf_fiber {
	unsigned int		ref_count;
	unsigned int		flags;
	tf_uctx_t		context;
	tf_uctx_t		return_context;
	struct tf_list_node	queue_node;
	struct tf_list_head	wakeup_q;
	struct tf_timeout_client timeout;
	struct tf_ifc *		pending_ifc;
	char			data[TF_EMPTY_ARRAY];
};

typedef void (*tf_fiber_proc)(void *fiber);

void *__tf_fiber_create(tf_fiber_proc fiber_main, int private_size);
void *tf_fiber_create(tf_fiber_proc fiber_main, int private_size);
void *tf_fiber_get(void *fiber);
void tf_fiber_put(void *fiber);
int tf_fiber_run(void *fiber);
void tf_fiber_kill(void *fiber);

void tf_fiber_wakeup(struct tf_fiber *fiber);
int  tf_fiber_schedule(void);

void tf_fiber_exit(void) attribute_noreturn;

static inline struct tf_fiber *tf_vmach_get_current_fiber(void)
{
	extern __thread struct tf_fiber *tf_current_fiber;
	return tf_current_fiber;
}

static inline void tf_timeout_push(struct tf_timeout *to, tf_mtime_diff_t ms)
{
	struct tf_fiber *f = tf_vmach_get_current_fiber();

	to->saved_timeout = f->timeout.value;
	to->my_timeout = tf_mtime_now() + ms;
	if (f->timeout.value == 0 ||
	    tf_mtime_diff(to->my_timeout, f->timeout.value) < 0)
		f->timeout.value = to->my_timeout;
}

static inline int tf_timeout_pop(struct tf_timeout *to, int err)
{
	struct tf_fiber *f = tf_vmach_get_current_fiber();

	f->timeout.value = to->saved_timeout;
	return err;
}

static inline
int tf_msleep(tf_mtime_diff_t ms)
{
	struct tf_timeout to;
	int r;

	tf_timeout_push(&to, ms);
	do {
		r = tf_fiber_schedule();
	} while (r != -ETIME);

	return tf_timeout_pop(&to, r);
}

#endif