summaryrefslogtreecommitdiff
path: root/usr/src/lib/libfakekernel/common/callout.c
blob: 6752e2f44ae3b0a54e2ee329b191704289d7abfc (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
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
 */

/*
 * Implement timeout(9f), untimeout(9f) on top of
 * libc timer_create, timer_settime, etc.
 */

#include <sys/types.h>
#include <sys/systm.h>
#include <time.h>

typedef void (*sigev_notify_func_t)(union sigval);

/*
 * We never actually reference anything in this array, using it
 * just as a collection of addresses mapped from/to int values.
 * It would be fine to take addresses even beyond the end, but
 * to avoid confusion it's sized larger than _TIMER_MAX (32).
 */
static char timeout_base[100];

timeout_id_t
timeout(void (*func)(void *), void *arg, clock_t delta)
{
	struct sigevent sev;
	struct itimerspec its;
	timer_t tid;
	int err;

	if (delta <= 0)
		return (NULL);

	bzero(&sev, sizeof (sev));
	sev.sigev_notify = SIGEV_THREAD;
	sev.sigev_value.sival_ptr = arg;
	sev.sigev_notify_function = (sigev_notify_func_t)(uintptr_t)func;
	err = timer_create(CLOCK_REALTIME, &sev, &tid);
	if (err != 0)
		return (NULL);

	bzero(&its, sizeof (its));
	TICK_TO_TIMESTRUC(delta, &its.it_value);
	err = timer_settime(tid, 0, &its, NULL);
	if (err != 0) {
		(void) timer_delete(tid);
		return (NULL);
	}

	/* Convert return to a (sort of) pointer */
	return (timeout_base + tid);
}

clock_t
untimeout(timeout_id_t id_arg)
{
	struct itimerspec its, oits;
	char *id_cp = id_arg;
	clock_t delta;
	timer_t tid;
	int rc;

	if (id_arg == NULL)
		return (-1);

	/* Convert id_arg back to small integer. */
	tid = (int)(id_cp - timeout_base);

	bzero(&its, sizeof (its));
	bzero(&oits, sizeof (oits));
	rc = timer_settime(tid, 0, &its, &oits);
	if (rc != 0) {
		delta = 0;
	} else {
		delta = TIMESTRUC_TO_TICK(&oits.it_value);
		if (delta < 0)
			delta = 0;
	}

	rc = timer_delete(tid);
	if (rc != 0)
		delta = -1;

	return (delta);
}