summaryrefslogtreecommitdiff
path: root/libvxi11client/perlbits/perlglue.c
blob: 5c9f4c28a0dea5cd785b6a969c42a2c01a75d078 (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
#include <EXTERN.h>
#include <perl.h>
#include <glib.h>
#include "libvxi11client.h"

#define DEBUG

#ifdef DEBUG
#include <stdio.h>
#endif

// thread safe queue for stacking up interrupts
static GAsyncQueue* interruptqueue;

typedef struct {
	gchar* handle;
} Event;

static Event* lastevent = NULL;

// perl is responsible for freeing the handle
static void freeevent(Event* event) {
	g_free(event);
}

static void freelast() {
	if (lastevent != NULL ) {
		freeevent(lastevent);
		lastevent = NULL;
	}
}

/**
 * This is the callback that is passed to the interrupt
 * thread for it to run each time it gets an interrupt.
 * It creates a new event, puts the handle in place and
 * puts in the in the queue for someone to collect later
 */

static void interruptcallback(gchar* handle) {
	Event* event = g_malloc(sizeof(Event));
	event->handle = handle;
	g_async_queue_push(interruptqueue, event);
}

/**
 * This is just for perl, it creates a new "context" and passes
 * that to the real open function
 */

VXI11Context* glue_open(char* address, char* device) {
	VXI11Context* context = g_malloc(sizeof(VXI11Context));
	if (context == NULL )
		return NULL ;

	int ret = vxi11_open(context, address, device);
	// if the connection fails dump the context and return null
	// so perl can see what happened
	if (ret < 1) {
		g_free(context);
		context = NULL;
	}

	return context;
}

/**
 * Create a queue, start the interrupt server, and give it our callback
 */

int glue_start_interrupt_server() {
	interruptqueue = g_async_queue_new();
	g_async_queue_ref(interruptqueue);
	return vxi11_start_interrupt_server(interruptcallback);
}

/**
 * Close down the interrupt server and free everything
 */

int glue_stop_interrupt_server() {
	int ret = vxi11_stop_interrupt_server();
	Event* event = NULL;
	// clear everything that is left over
	while ((event = (Event*) g_async_queue_try_pop(interruptqueue)) != NULL ) {
		freeevent(event);
	}
	freelast();
	g_async_queue_unref(interruptqueue);
	interruptqueue = NULL;
	return ret;
}

/**
 * Block until an interrupt happens. A time out of -1 while block until an
 * interrupt happens or forever(!), 0 will not block and only return a handle
 * if an interrupt already happened, any value > 0 will return instantly if there
 * is a queued interrupt or wait for one to happen up to the timeout.
 */

char* glue_wait_for_interrupt(int timeout) {
	if (interruptqueue == NULL ) {
#ifdef DEBUG
		printf("interrupt queue is null!\n");
#endif
		return NULL ;
	}

	// if timeout > 0 pop with a time out,
	// if the timeout is 0 try to pop but don't block
	// if the timeout is -1 block until an interrupt happens
	freelast();

	if (timeout > 0) {
		GTimeVal whentotimeout;
		g_get_current_time(&whentotimeout);
		g_time_val_add(&whentotimeout, timeout * 1000);
		lastevent = (Event*) g_async_queue_timed_pop(interruptqueue, &whentotimeout);
	}
	else if (timeout == 0) {
		lastevent = (Event*) g_async_queue_try_pop(interruptqueue);
	}
	else if (timeout == -1) {
		lastevent = (Event*) g_async_queue_pop(interruptqueue);
	}

	if (lastevent != NULL ) {
		return lastevent->handle;
	}
	else
		return NULL ;
}