mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add ConditionVariable.wait_until and ConditionVariable.wait_for (#2302)
* Add `ConditionVariable.wait_until` and `ConditionVariable.wait_for` * Add "@structlike" for typedefs. --------- Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
This commit is contained in:
committed by
GitHub
parent
448176b0b7
commit
2053f2767b
@@ -96,6 +96,8 @@ macro anycast(any v, $Type) @builtin
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo);
|
||||
|
||||
macro @addr(#val) @builtin
|
||||
{
|
||||
$if $defined(&#val):
|
||||
@@ -104,6 +106,7 @@ macro @addr(#val) @builtin
|
||||
return &&#val;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
{
|
||||
void*[256] buffer;
|
||||
|
||||
@@ -20,7 +20,6 @@ macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
|
||||
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
||||
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
|
||||
macro bool @is_lvalue(#value) => $defined(#value = #value);
|
||||
macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo);
|
||||
macro bool @is_const(#foo) @const @builtin
|
||||
{
|
||||
var $v;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module libc;
|
||||
|
||||
@@ -4,7 +4,7 @@ import std::time;
|
||||
<*
|
||||
Return a "timespec" from a duration.
|
||||
|
||||
@require self >= 0
|
||||
@require self >= time::NANO_DURATION_ZERO
|
||||
*>
|
||||
fn TimeSpec NanoDuration.to_timespec(self) @inline
|
||||
{
|
||||
@@ -16,7 +16,7 @@ fn TimeSpec NanoDuration.to_timespec(self) @inline
|
||||
<*
|
||||
Convert a duration to a timespec.
|
||||
|
||||
@require self >= 0
|
||||
@require self >= time::DURATION_ZERO
|
||||
*>
|
||||
fn TimeSpec Duration.to_timespec(self) @inline
|
||||
{
|
||||
@@ -24,3 +24,11 @@ fn TimeSpec Duration.to_timespec(self) @inline
|
||||
Time_t sec = (Time_t)(self / time::SEC);
|
||||
return { .s = sec, .ns = ns };
|
||||
}
|
||||
|
||||
<*
|
||||
Convert a timestamp to a timespec.
|
||||
*>
|
||||
fn TimeSpec Time.to_timespec(self) @inline
|
||||
{
|
||||
return ((Duration)self).to_timespec();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
|
||||
}
|
||||
}
|
||||
|
||||
const Duration POLL_FOREVER = -1;
|
||||
const Duration POLL_FOREVER = (Duration)-1;
|
||||
|
||||
typedef PollSubscribes = ushort;
|
||||
typedef PollEvents = ushort;
|
||||
|
||||
@@ -5,11 +5,11 @@ import std::time, libc;
|
||||
typedef TcpSocket = inline Socket;
|
||||
typedef TcpServerSocket = inline Socket;
|
||||
|
||||
fn TcpSocket? connect(String host, uint port, Duration timeout = 0, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
|
||||
fn TcpSocket? connect(String host, uint port, Duration timeout = time::DURATION_ZERO, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
|
||||
{
|
||||
AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!;
|
||||
defer os::freeaddrinfo(ai);
|
||||
if (timeout > 0)
|
||||
if (timeout > time::DURATION_ZERO)
|
||||
{
|
||||
return (TcpSocket)net::connect_with_timeout_from_addrinfo(ai, options, timeout)!;
|
||||
}
|
||||
|
||||
@@ -142,12 +142,26 @@ fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx)
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
|
||||
{
|
||||
TimeSpec now;
|
||||
if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return thread::WAIT_FAILED?;
|
||||
now.ns += (CLong)((ms % 1000) * 1000_000);
|
||||
now.s += (Time_t)(ms / 1000 + now.ns / 1000_000_000);
|
||||
now.ns = now.ns % 1000_000_000;
|
||||
switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &now))
|
||||
Time time = time::now() + time::ms(ms);
|
||||
return cond.wait_until(mtx, time) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.is_initialized()
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait_timeout_duration(&cond, NativeMutex* mtx, Duration duration)
|
||||
{
|
||||
if (duration < time::DURATION_ZERO) return thread::WAIT_TIMEOUT?;
|
||||
Time time = time::now() + duration;
|
||||
return cond.wait_until(mtx, time) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.is_initialized()
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time)
|
||||
{
|
||||
switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &&time.to_timespec()))
|
||||
{
|
||||
case errno::ETIMEDOUT:
|
||||
return thread::WAIT_TIMEOUT?;
|
||||
@@ -216,7 +230,7 @@ fn void native_thread_yield()
|
||||
|
||||
fn void? native_sleep_nano(NanoDuration nano)
|
||||
{
|
||||
if (nano <= 0) return;
|
||||
if (nano <= time::NANO_DURATION_ZERO) return;
|
||||
if (libc::nanosleep(&&nano.to_timespec(), null)) return thread::INTERRUPTED?;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ fn void? NativeTimedMutex.lock_timeout(&mtx, ulong ms)
|
||||
|
||||
NanoDuration duration = time::ms(ms).to_nano();
|
||||
Clock start = clock::now();
|
||||
for (NanoDuration remaining = duration; remaining > 0; remaining = duration - start.to_now())
|
||||
for (NanoDuration remaining = duration; remaining > time::NANO_DURATION_ZERO; remaining = duration - start.to_now())
|
||||
{
|
||||
ulong remaining_ms = remaining.to_ms();
|
||||
if (remaining_ms > uint.max) remaining_ms = uint.max;
|
||||
@@ -318,6 +318,26 @@ fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
|
||||
return timedwait(cond, mtx, (uint)ms) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait_timeout_duration(&cond, NativeMutex* mtx, Duration duration) @inline
|
||||
{
|
||||
if (duration < time::DURATION_ZERO) return thread::WAIT_TIMEOUT?;
|
||||
long ms = duration.to_ms();
|
||||
if (ms > uint.max) ms = uint.max;
|
||||
return timedwait(cond, mtx, (uint)ms) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time) @inline
|
||||
{
|
||||
Duration duration = time - time::now();
|
||||
return cond.wait_timeout_duration(mtx, duration);
|
||||
}
|
||||
|
||||
fn void? NativeThread.create(&thread, ThreadFn func, void* args)
|
||||
{
|
||||
if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return thread::INIT_FAILED?;
|
||||
|
||||
@@ -63,9 +63,21 @@ macro void? ConditionVariable.wait(&cond, Mutex* mutex)
|
||||
{
|
||||
return NativeConditionVariable.wait((NativeConditionVariable*)cond, (NativeMutex*)mutex);
|
||||
}
|
||||
macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, ulong ms)
|
||||
<*
|
||||
@require @assignable_to(#ms_or_duration, Duration) || @assignable_to(#ms_or_duration, ulong)
|
||||
*>
|
||||
macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, #ms_or_duration) @safemacro
|
||||
{
|
||||
return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, ms);
|
||||
$if @implicit_to(#ms_or_duration):
|
||||
return NativeConditionVariable.wait_timeout_duration((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration);
|
||||
$else
|
||||
return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro void? ConditionVariable.wait_until(&cond, Mutex* mutex, Time time)
|
||||
{
|
||||
return NativeConditionVariable.wait_until((NativeConditionVariable*)cond, (NativeMutex*)mutex, time);
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -12,10 +12,10 @@ fn Clock native_clock()
|
||||
ulong mult = 0;
|
||||
if (!freq.quadPart)
|
||||
{
|
||||
if (!win32::queryPerformanceFrequency(&freq)) return 0;
|
||||
if (!win32::queryPerformanceFrequency(&freq)) return (Clock)0;
|
||||
}
|
||||
Win32_LARGE_INTEGER counter @noinit;
|
||||
if (!win32::queryPerformanceCounter(&counter)) return 0;
|
||||
if (!win32::queryPerformanceCounter(&counter)) return (Clock)0;
|
||||
return (Clock)counter.quadPart.muldiv(1_000_000_000, freq.quadPart);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
module std::time;
|
||||
import std::io, std::time::os;
|
||||
|
||||
typedef Time = long;
|
||||
typedef Duration = long;
|
||||
typedef Clock = ulong;
|
||||
typedef NanoDuration (Printable) = long;
|
||||
typedef Time @structlike = long;
|
||||
typedef Duration @structlike = long;
|
||||
typedef Clock @structlike = ulong;
|
||||
typedef NanoDuration (Printable) @structlike = long;
|
||||
|
||||
const Time FAR_FUTURE = long.max;
|
||||
const Time FAR_PAST = long.min;
|
||||
|
||||
const NanoDuration NANO_DURATION_ZERO = 0;
|
||||
const Duration US = 1;
|
||||
const Duration MS = 1_000;
|
||||
const Duration SEC = 1_000_000;
|
||||
@@ -18,6 +20,7 @@ const Duration WEEK = 7 * DAY;
|
||||
const Duration MONTH = 30 * DAY;
|
||||
const Duration YEAR = 36525 * DAY / 100;
|
||||
const Duration FOREVER = long.max;
|
||||
const Duration DURATION_ZERO = 0;
|
||||
|
||||
fn Duration us(long l) @inline => l * US;
|
||||
fn Duration ms(long l) @inline => l * MS;
|
||||
@@ -78,7 +81,7 @@ fn Time now()
|
||||
$if $defined(native_timestamp):
|
||||
return os::native_timestamp();
|
||||
$else
|
||||
return 0;
|
||||
return (Time)0;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -114,31 +117,31 @@ macro Duration Duration.mult(#td, long #val) @operator_s(*) @safemacro => (Durat
|
||||
fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
NanoDuration nd = *self;
|
||||
if (nd == 0)
|
||||
if (nd == NANO_DURATION_ZERO)
|
||||
{
|
||||
return formatter.printf("0s")!;
|
||||
}
|
||||
|
||||
bool neg = nd < 0;
|
||||
bool neg = nd < NANO_DURATION_ZERO;
|
||||
if (neg) nd = -nd;
|
||||
|
||||
DString str = dstring::temp_with_capacity(64);
|
||||
if (nd < 1_000_000_000)
|
||||
if (nd < (NanoDuration)1_000_000_000)
|
||||
{
|
||||
// Less than 1s: print milliseconds, microseconds and nanoseconds.
|
||||
NanoDuration ms = nd / 1_000_000;
|
||||
if (ms > 0)
|
||||
if (ms > NANO_DURATION_ZERO)
|
||||
{
|
||||
str.appendf("%dms", ms);
|
||||
nd -= ms * 1_000_000;
|
||||
}
|
||||
NanoDuration us = nd / 1000;
|
||||
if (us > 0)
|
||||
if (us > NANO_DURATION_ZERO)
|
||||
{
|
||||
str.appendf("%dµs", us);
|
||||
nd -= us * 1000;
|
||||
}
|
||||
if (nd > 0)
|
||||
if (nd > NANO_DURATION_ZERO)
|
||||
{
|
||||
str.appendf("%dns", nd);
|
||||
}
|
||||
@@ -150,19 +153,19 @@ fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
|
||||
|
||||
nd /= 1_000_000_000;
|
||||
NanoDuration hour = nd / 3600;
|
||||
if (hour > 0)
|
||||
if (hour > NANO_DURATION_ZERO)
|
||||
{
|
||||
str.appendf("%dh", hour);
|
||||
nd -= hour * 3600;
|
||||
}
|
||||
NanoDuration min = nd / 60;
|
||||
if (min > 0)
|
||||
if (min > NANO_DURATION_ZERO)
|
||||
{
|
||||
str.appendf("%dm", min);
|
||||
nd -= min * 60;
|
||||
}
|
||||
NanoDuration sec = nd;
|
||||
if (ms > 0)
|
||||
if (ms > NANO_DURATION_ZERO)
|
||||
{
|
||||
// Ignore trailing zeroes.
|
||||
while (ms / 10 * 10 == ms) ms /= 10;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
- Implicit linking of libc math when libc math functions are used.
|
||||
- Allow even smaller memory limits.
|
||||
- Check unaligned array access.
|
||||
- Add "@structlike" for typedefs.
|
||||
|
||||
### Fixes
|
||||
- mkdir/rmdir would not work properly with substring paths on non-windows platforms.
|
||||
@@ -74,6 +75,7 @@
|
||||
- New virtual emory arena allocator.
|
||||
- Added `WString.len`.
|
||||
- Added `@addr` macro.
|
||||
- Add `ConditionVariable.wait_until` and `ConditionVariable.wait_for`
|
||||
|
||||
## 0.7.3 Change list
|
||||
|
||||
|
||||
@@ -646,6 +646,7 @@ typedef struct Decl_
|
||||
bool attr_compact : 1;
|
||||
bool resolved_attributes : 1;
|
||||
bool allow_deprecated : 1;
|
||||
bool attr_structlike : 1;
|
||||
union
|
||||
{
|
||||
void *backend_ref;
|
||||
|
||||
@@ -309,6 +309,7 @@ typedef enum
|
||||
ATTRIBUTE_REFLECT,
|
||||
ATTRIBUTE_SAFEMACRO,
|
||||
ATTRIBUTE_SECTION,
|
||||
ATTRIBUTE_STRUCTLIKE,
|
||||
ATTRIBUTE_TAG,
|
||||
ATTRIBUTE_TEST,
|
||||
ATTRIBUTE_UNUSED,
|
||||
|
||||
@@ -1393,7 +1393,7 @@ static bool rule_to_distinct(CastContext *cc, bool is_explicit, bool is_silent)
|
||||
{
|
||||
is_const = true;
|
||||
}
|
||||
if (is_const)
|
||||
if (is_const && (cc->is_binary_conversion || !cc->to->decl->attr_structlike))
|
||||
{
|
||||
cc->to = flat;
|
||||
cc->to_group = flat_group;
|
||||
|
||||
@@ -3112,6 +3112,7 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
|
||||
[ATTRIBUTE_REFLECT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES,
|
||||
[ATTRIBUTE_SAFEMACRO] = ATTR_MACRO,
|
||||
[ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL,
|
||||
[ATTRIBUTE_STRUCTLIKE] = ATTR_DISTINCT,
|
||||
[ATTRIBUTE_TAG] = ATTR_BITSTRUCT_MEMBER | ATTR_MEMBER | USER_DEFINED_TYPES | CALLABLE_TYPE,
|
||||
[ATTRIBUTE_TEST] = ATTR_FUNC,
|
||||
[ATTRIBUTE_UNUSED] = (AttributeDomain)~(ATTR_CALL),
|
||||
@@ -3433,6 +3434,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
|
||||
}
|
||||
if (!decl->func_decl.priority) decl->func_decl.priority = MAX_PRIORITY;
|
||||
return true;
|
||||
case ATTRIBUTE_STRUCTLIKE:
|
||||
decl->attr_structlike = true;
|
||||
return true;
|
||||
case ATTRIBUTE_SECTION:
|
||||
case ATTRIBUTE_EXTERN:
|
||||
if (context->unit->module->is_generic)
|
||||
|
||||
@@ -373,6 +373,7 @@ void symtab_init(uint32_t capacity)
|
||||
attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect");
|
||||
attribute_list[ATTRIBUTE_SAFEMACRO] = KW_DEF("@safemacro");
|
||||
attribute_list[ATTRIBUTE_SECTION] = KW_DEF("@section");
|
||||
attribute_list[ATTRIBUTE_STRUCTLIKE] = KW_DEF("@structlike");
|
||||
attribute_list[ATTRIBUTE_TEST] = KW_DEF("@test");
|
||||
attribute_list[ATTRIBUTE_TAG] = KW_DEF("@tag");
|
||||
attribute_list[ATTRIBUTE_UNUSED] = KW_DEF("@unused");
|
||||
|
||||
@@ -66,8 +66,8 @@ fn void test_timezone()
|
||||
Time t1 = d1.time;
|
||||
Time t2 = d2.time;
|
||||
|
||||
assert(t1 == 1665792000000000);
|
||||
assert(t2 == 1665766800000000);
|
||||
assert(t1 == (Time)1665792000000000);
|
||||
assert(t2 == (Time)1665766800000000);
|
||||
|
||||
// to_gmt_offset should keep the timesampt value
|
||||
tz = d1.to_gmt_offset(offset);
|
||||
|
||||
Reference in New Issue
Block a user