aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/driver_SUITE_data/timer_drv.c
blob: c3ce3b6e495d064c3d4bcf9684f32078bf6c1d20 (plain) (blame)
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
#include <stdio.h>
#include "erl_driver.h"
#ifdef __WIN32__
#  include <windows.h>
#else
#  include <sys/time.h>
#  include <sys/types.h>
#  include <sys/select.h>
#  include <unistd.h>
#endif

#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
                      (((unsigned char*) (s))[1] << 16) | \
                      (((unsigned char*) (s))[2] << 8)  | \
                      (((unsigned char*) (s))[3]))

#define START_TIMER 0
#define CANCEL_TIMER 1
#define DELAY_START_TIMER 2
#define TIMER 3
#define CANCELLED 4

static ErlDrvPort erlang_port;
static ErlDrvData timer_start(ErlDrvPort, char*);
static void timer_stop(ErlDrvData);
static void timer_read(ErlDrvData, char*, ErlDrvSizeT);
static void timer(ErlDrvData);
static void ms_sleep(int ms);

static ErlDrvEntry timer_driver_entry =
{
    NULL,
    timer_start,
    timer_stop,
    timer_read,
    NULL,
    NULL,
    "timer_drv",
    NULL,
    NULL,
    NULL,
    timer,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ERL_DRV_EXTENDED_MARKER,
    ERL_DRV_EXTENDED_MAJOR_VERSION,
    ERL_DRV_EXTENDED_MINOR_VERSION,
    0,
    NULL,
    NULL,
    NULL
};

DRIVER_INIT(timer_drv)
{
    erlang_port = (ErlDrvPort)-1;
    return &timer_driver_entry;
}

static ErlDrvData timer_start(ErlDrvPort port, char *buf)
{
    if (erlang_port != (ErlDrvPort)-1) {
	return ERL_DRV_ERROR_GENERAL;
    }
    erlang_port = port;
    return (ErlDrvData)port;
}

/* set the timer, this is monitored from erlang measuring the time */
static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
{
    ErlDrvPort port = (ErlDrvPort) p;
    char reply[1];

    if (buf[0] == START_TIMER) { 
	/* fprintf(stderr, "[timer_drv] Setting timeout: %i\n", get_int32(buf + 1)); */
	driver_set_timer(port, get_int32(buf + 1));
    } else if (buf[0] == CANCEL_TIMER) {
	/*	fprintf(stderr, "[timer_drv] Timer cancelled\n"); */
	driver_cancel_timer(port);
	reply[0] = CANCELLED;
	driver_output(port, reply, 1);
    } else if (buf[0] == DELAY_START_TIMER) {
	ms_sleep(1000);
	driver_set_timer(port, get_int32(buf + 1));
    }
}

static void timer_stop(ErlDrvData port)
{
    erlang_port = (ErlDrvPort)-1;
}

static void timer(ErlDrvData port)
{
    char reply[1];

    /*   fprintf(stderr, "[timer_drv] timer timed out\n"); */
    reply[0] = TIMER;
    driver_output((ErlDrvPort)port, reply, 1);
}

static void
ms_sleep(int ms)
{
    /* Important that we do not return too early... */
    ErlDrvTime time, timeout_time;

    time = erl_drv_monotonic_time(ERL_DRV_USEC);

    timeout_time = time + ((ErlDrvTime) ms)*1000;

    while (time < timeout_time) {
	ErlDrvTime timeout = timeout_time - time;

#ifdef __WIN32__
	Sleep((DWORD) (timeout / 1000));
#else
	{
	    struct timeval tv;

	    tv.tv_sec = (long) timeout / (1000*1000);
	    tv.tv_usec = (long) timeout % (1000*1000);

	    select(0, NULL, NULL, NULL, &tv);
	}
#endif

	time = erl_drv_monotonic_time(ERL_DRV_USEC);
    }

}