OVMS3/OVMS.V3/components/duktape/examples/eventloop/c_eventloop.js

192 lines
5.4 KiB
JavaScript

/*
* C eventloop example (c_eventloop.c).
*
* ECMAScript code to initialize the exposed API (setTimeout() etc) when
* using the C eventloop.
*
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Timers
*/
/*
* Timer API
*/
function setTimeout(func, delay) {
var cb_func;
var bind_args;
var timer_id;
// Delay can be optional at least in some contexts, so tolerate that.
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
if (typeof delay !== 'number') {
if (typeof delay === 'undefined') {
delay = 0;
} else {
throw new TypeError('invalid delay');
}
}
if (typeof func === 'string') {
// Legacy case: callback is a string.
cb_func = eval.bind(this, func);
} else if (typeof func !== 'function') {
throw new TypeError('callback is not a function/string');
} else if (arguments.length > 2) {
// Special case: callback arguments are provided.
bind_args = Array.prototype.slice.call(arguments, 2); // [ arg1, arg2, ... ]
bind_args.unshift(this); // [ global(this), arg1, arg2, ... ]
cb_func = func.bind.apply(func, bind_args);
} else {
// Normal case: callback given as a function without arguments.
cb_func = func;
}
timer_id = EventLoop.createTimer(cb_func, delay, true /*oneshot*/);
return timer_id;
}
function clearTimeout(timer_id) {
if (typeof timer_id !== 'number') {
throw new TypeError('timer ID is not a number');
}
var success = EventLoop.deleteTimer(timer_id); /* retval ignored */
}
function setInterval(func, delay) {
var cb_func;
var bind_args;
var timer_id;
if (typeof delay !== 'number') {
if (typeof delay === 'undefined') {
delay = 0;
} else {
throw new TypeError('invalid delay');
}
}
if (typeof func === 'string') {
// Legacy case: callback is a string.
cb_func = eval.bind(this, func);
} else if (typeof func !== 'function') {
throw new TypeError('callback is not a function/string');
} else if (arguments.length > 2) {
// Special case: callback arguments are provided.
bind_args = Array.prototype.slice.call(arguments, 2); // [ arg1, arg2, ... ]
bind_args.unshift(this); // [ global(this), arg1, arg2, ... ]
cb_func = func.bind.apply(func, bind_args);
} else {
// Normal case: callback given as a function without arguments.
cb_func = func;
}
timer_id = EventLoop.createTimer(cb_func, delay, false /*oneshot*/);
return timer_id;
}
function clearInterval(timer_id) {
if (typeof timer_id !== 'number') {
throw new TypeError('timer ID is not a number');
}
EventLoop.deleteTimer(timer_id);
}
function requestEventLoopExit() {
EventLoop.requestExit();
}
/*
* Socket handling
*
* Ideally this would be implemented more in C than here for more speed
* and smaller footprint: C code would directly maintain the callback state
* and such.
*
* Also for more optimal I/O, the buffer churn caused by allocating and
* freeing a lot of buffer values could be eliminated by reusing buffers.
* Socket reads would then go into a pre-allocated buffer, for instance.
*/
EventLoop.socketListening = {};
EventLoop.socketReading = {};
EventLoop.socketConnecting = {};
EventLoop.fdPollHandler = function(fd, revents) {
var data;
var cb;
var rc;
var acc_res;
//print('activity on fd', fd, 'revents', revents);
if (revents & Poll.POLLIN) {
cb = this.socketReading[fd];
if (cb) {
data = Socket.read(fd); // no size control now
//print('READ', Duktape.enc('jx', data));
if (data.length === 0) {
this.close(fd);
return;
}
cb(fd, data);
} else {
cb = this.socketListening[fd];
if (cb) {
acc_res = Socket.accept(fd);
//print('ACCEPT:', Duktape.enc('jx', acc_res));
cb(acc_res.fd, acc_res.addr, acc_res.port);
} else {
//print('UNKNOWN');
}
}
}
if (revents & Poll.POLLOUT) {
// Connected
cb = this.socketConnecting[fd];
if (cb) {
delete this.socketConnecting[fd];
cb(fd);
}
}
if ((revents & ~(Poll.POLLIN | Poll.POLLOUT)) !== 0) {
//print('unexpected revents, close fd');
this.close(fd);
}
}
EventLoop.server = function(address, port, cb_accepted) {
var fd = Socket.createServerSocket(address, port);
this.socketListening[fd] = cb_accepted;
this.listenFd(fd, Poll.POLLIN);
}
EventLoop.connect = function(address, port, cb_connected) {
var fd = Socket.connect(address, port);
this.socketConnecting[fd] = cb_connected;
this.listenFd(fd, Poll.POLLOUT);
}
EventLoop.close = function(fd) {
EventLoop.listenFd(fd, 0);
delete this.socketListening[fd];
delete this.socketReading[fd];
delete this.socketConnecting[fd];
Socket.close(fd);
}
EventLoop.setReader = function(fd, cb_read) {
this.socketReading[fd] = cb_read;
this.listenFd(fd, Poll.POLLIN);
}
EventLoop.write = function(fd, data) {
// This simple example doesn't have support for write blocking / draining
if (typeof data === 'string') {
data = new TextEncoder().encode(data);
}
var rc = Socket.write(fd, data);
}