Skip to content

Commit 44e4c65

Browse files
committed
Introduce os.sleep and os.now APIs
1 parent b38db73 commit 44e4c65

File tree

6 files changed

+213
-21
lines changed

6 files changed

+213
-21
lines changed

docs/pages/libraries.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,41 @@ In POSIX systems, this function also creates a file with that name, to avoid sec
14171417
You still have to open the file to use it and to remove it (even if you do not use it).
14181418
When possible, you may prefer to use `io.tmpfile`, which automatically removes the file when the program ends.
14191419

1420+
### os.now
1421+
1422+
```nelua
1423+
function os.now(): number
1424+
```
1425+
1426+
Get time elapsed in seconds since its first call using a high resolution timer.
1427+
Returns a number greater or equal than `0` on success, otherwise `-1`.
1428+
1429+
In the first successful call `0` is returned,
1430+
in subsequent calls the relative time in seconds since the first call is returned.
1431+
This is typically used to compute time differences with high precision.
1432+
1433+
The time resolution is unspecified and depends on the OS,
1434+
but typically has nanosecond precision on POSIX systems.
1435+
1436+
The operation may not be supported by all systems, or may fail in some systems,
1437+
in that case `-1` is returned.
1438+
1439+
### os.sleep
1440+
1441+
```nelua
1442+
function os.sleep(secs: number): boolean
1443+
```
1444+
1445+
Sleep the current OS thread for `secs` seconds.
1446+
Returns true on success, otherwise false.
1447+
1448+
The operation typically has at least millisecond precision,
1449+
the sleep time will be typically the requested one,
1450+
but can be a little lower or higher depending on the system.
1451+
1452+
The operation may not be supported by all systems, or may fail in some systems,
1453+
in that case false is returned.
1454+
14201455
---
14211456
## span
14221457

@@ -1758,7 +1793,7 @@ Return length of a string. Used by the length operator (`#`).
17581793
### string.__concat
17591794

17601795
```nelua
1761-
function string.__concat(a: string_coercion_concept, b: string_coercion_concept): string
1796+
function string.__concat(a: auto, b: auto): string
17621797
```
17631798

17641799
Concatenate two strings. Used by the concatenation operator (`..`).
@@ -4328,7 +4363,7 @@ or if the system does not have an allocator.
43284363

43294364
Its memory cannot grow automatically, use the system's general purpose allocator for that.
43304365
The allocator is not thread safe, it was designed to be used in single thread applications.
4331-
Allocations are always 16 byte aligned.
4366+
Allocations are always aligned to the platform max alignment, typically 16 bytes.
43324367

43334368
*NOTE*: This is experimental, a bunch of tests were done but is not really battle tested.
43344369

lib/io.nelua

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,19 @@ function io.popen(prog: string, mode: facultative(string)) : (filestream, string
8787
##[==[
8888
cinclude '<stdio.h>' -- for popen/pclose
8989
cinclude '<stddef.h>' -- for NULL
90+
cinclude [[
91+
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
92+
#include <unistd.h>
93+
#endif
94+
]]
9095
cemit[[
91-
#if !defined(_WIN32) && defined(__unix__)
96+
#ifdef _WIN32
97+
fp = _popen(progcs, modecs);
98+
closef = _pclose;
99+
#elif _POSIX_VERSION >= 200112L
92100
fflush(NULL);
93101
fp = popen(progcs, modecs);
94102
closef = pclose;
95-
#else
96-
fp = _popen(progcs, modecs);
97-
closef = _pclose;
98103
#endif
99104
]]
100105
]==]

lib/os.nelua

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,13 +280,13 @@ function os.tmpname(): string <polymorphic>
280280
cinclude '<stdlib.h>' -- for getenv
281281
cinclude '<stdio.h>' -- for tmpnam
282282
cinclude '<stdbool.h>' -- for true/false
283-
cemitdecl[[
284-
#if !defined(_WIN32) && defined(__unix__)
285-
#include <unistd.h>
283+
cinclude [[
284+
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
285+
#include <unistd.h>
286286
#endif
287287
]]
288-
cemit[[
289-
#if !defined(_WIN32) && defined(__unix__)
288+
cemit [[
289+
#if _POSIX_VERSION >= 200112L
290290
char* tmpdir = getenv("TMPDIR");
291291
if(tmpdir != NULL) {
292292
strncpy(bufcs, tmpdir, 260);
@@ -312,3 +312,142 @@ function os.tmpname(): string <polymorphic>
312312
return (@string){}
313313
end
314314
end
315+
316+
--[[
317+
Get time elapsed in seconds since its first call using a high resolution timer.
318+
Returns a number greater or equal than `0` on success, otherwise `-1`.
319+
320+
In the first successful call `0` is returned,
321+
in subsequent calls the relative time in seconds since the first call is returned.
322+
This is typically used to compute time differences with high precision.
323+
324+
The time resolution is unspecified and depends on the OS,
325+
but typically has nanosecond precision on POSIX systems.
326+
327+
The operation may not be supported by all systems, or may fail in some systems,
328+
in that case `-1` is returned.
329+
]]
330+
function os.now(): number <polymorphic>
331+
local ok: boolean = false
332+
local ns: int64
333+
##[==[
334+
cinclude '<time.h>' -- for timespec_get/TIME_UTC
335+
cinclude '<stdbool.h>' -- for bool
336+
cinclude '<stdint.h>' -- for int64_t
337+
cinclude [[
338+
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
339+
#include <unistd.h>
340+
#endif
341+
]]
342+
cemitdecl [[
343+
#if __STDC_VERSION__ >= 201112L && defined(TIME_UTC)
344+
#define _HAVE_C11_TIMESPEC_GET
345+
#elif defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
346+
#define _HAVE_POSIX_MONOTONIC_CLOCK
347+
#elif _POSIX_VERSION >= 200112L
348+
#include <sys/time.h>
349+
#define _HAVE_POSIX_GETTIMEOFDAY
350+
#endif
351+
]]
352+
cemit [[
353+
#if defined(_HAVE_C11_TIMESPEC_GET)
354+
static bool initialized = false;
355+
static struct timespec start = {0};
356+
errno = 0;
357+
if(!initialized) {
358+
initialized = true;
359+
ok = timespec_get(&start, TIME_UTC) == TIME_UTC;
360+
ns = 0;
361+
} else {
362+
struct timespec ts = {0};
363+
ok = timespec_get(&ts, TIME_UTC) == TIME_UTC;
364+
ns = (int64_t)(ts.tv_sec - start.tv_sec)*1000000000 + (int64_t)(ts.tv_nsec - start.tv_nsec);
365+
}
366+
#elif defined(_HAVE_POSIX_MONOTONIC_CLOCK)
367+
static bool initialized = false;
368+
static struct timespec start = {0};
369+
errno = 0;
370+
if(!initialized) {
371+
initialized = true;
372+
ok = clock_gettime(CLOCK_MONOTONIC, &start) == 0;
373+
ns = 0;
374+
} else {
375+
struct timespec ts = {0};
376+
ok = clock_gettime(CLOCK_MONOTONIC, &ts) == 0;
377+
ns = (int64_t)(ts.tv_sec - start.tv_sec)*1000000000 + (int64_t)(ts.tv_nsec - start.tv_nsec);
378+
}
379+
#elif defined(_HAVE_POSIX_GETTIMEOFDAY)
380+
static bool initialized = false;
381+
static struct timeval start = {0};
382+
errno = 0;
383+
if(!initialized) {
384+
initialized = true;
385+
ok = gettimeofday(&start, NULL) == 0;
386+
ns = 0;
387+
} else {
388+
struct timeval ts;
389+
ok = gettimeofday(&ts, NULL) == 0;
390+
ns = (int64_t)(ts.tv_sec - start.tv_sec)*1000000000 + (int64_t)(ts.tv_usec - start.tv_usec)*1000;
391+
}
392+
#endif
393+
]]
394+
]==]
395+
if not ok then return -1.0 end
396+
return ns / 1000000000.0
397+
end
398+
399+
--[[
400+
Sleep the current OS thread for `secs` seconds.
401+
Returns true on success, otherwise false.
402+
403+
The operation typically has at least millisecond precision,
404+
the sleep time will be typically the requested one,
405+
but can be a little lower or higher depending on the system.
406+
407+
The operation may not be supported by all systems, or may fail in some systems,
408+
in that case false is returned.
409+
]]
410+
function os.sleep(secs: number): boolean <polymorphic>
411+
local us: uint64 <nodce> = (@uint64)(secs * 1000000)
412+
local ok: boolean = false
413+
##[==[
414+
cinclude '<time.h>' -- for nanosleep
415+
cinclude '<stdbool.h>' -- for true
416+
cinclude '<errno.h>' -- for errno
417+
cinclude [[
418+
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
419+
#include <unistd.h>
420+
#endif
421+
]]
422+
cinclude [[
423+
#ifdef _WIN32
424+
#ifndef WIN32_LEAN_AND_MEAN
425+
#define WIN32_LEAN_AND_MEAN
426+
#endif
427+
#include <windows.h>
428+
#endif
429+
]]
430+
cemit [[
431+
#ifdef _WIN32
432+
unsigned int ms = (us + 999) / 1000;
433+
if(ms > 0) {
434+
Sleep(ms);
435+
}
436+
ok = true;
437+
#elif _POSIX_VERSION >= 200112L
438+
if(us > 0) {
439+
struct timespec ts;
440+
ts.tv_sec = us / 1000000;
441+
ts.tv_nsec = (us % 1000000) * 1000;
442+
int res;
443+
do {
444+
errno = 0;
445+
res = nanosleep(&ts, &ts);
446+
} while(res != 0 && errno == EINTR);
447+
ok = res == 0;
448+
}
449+
#endif
450+
]]
451+
]==]
452+
return ok
453+
end

nelua/ccontext.lua

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,23 @@ If the file name is not an absolute path and not between `<>` or `""`,
170170
then looks for files in the current source file directory.
171171
]]
172172
function CContext:ensure_include(name)
173-
-- normalize include name
174-
local incname = name
173+
local directives = self.directives
174+
if directives[name] then return end
175+
-- normalize include code
176+
local inccode = name
175177
local searchinc = false
176-
if not name:match('^["<].*[>"]$') then
177-
incname = '<'..name..'>'
178-
searchinc = true
178+
if not inccode:find('[#\n]') then
179+
if not name:match('^["<].*[>"]$') then
180+
inccode = '<'..name..'>'
181+
searchinc = true
182+
end
183+
inccode = '#include '..inccode..'\n'
184+
if directives[inccode] then return end
179185
end
180186
-- add include directive
181-
local directives = self.directives
182-
if directives[incname] then return end
183-
directives[incname] = true
184-
directives[#directives+1] = '#include '..incname..'\n'
187+
directives[inccode] = true
188+
directives[name] = true
189+
directives[#directives+1] = inccode
185190
-- make sure to add the include directory for that file
186191
if searchinc and not fs.isabs(name) then
187192
local dirpath = self:get_visiting_directory()

nelua/cdefs.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ compilers_flags['zig cc'] = compilers_flags.clang
180180
-- Code to detect target features.
181181
cdefs.target_info_code = [[
182182
/* OS */
183-
#if defined(__unix__) || defined(__unix)
183+
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
184184
is_unix = true;
185185
#endif
186186
#if defined(__linux__) || defined(__linux)

tests/os_test.nelua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ do -- os.time
5858
assert(os.time(os.timedesc{year=2020,month=7,day=18,hour=12,isdst=false}) > 0)
5959
end
6060

61+
do -- os.now/os.sleep
62+
assert(os.now() == 0.0)
63+
os.sleep(0.004)
64+
assert(os.now() >= 0.004)
65+
os.sleep(0.004)
66+
assert(os.now() >= 0.008)
67+
end
68+
6169
print 'os OK!'
6270

6371
do -- os.exit

0 commit comments

Comments
 (0)