mORMot2 DailyDaily read of upstream commits

Thread wake-up goes lockless, and two API moves worth grepping for

Today is mostly about the asynchronous server core. The thread-wakeup path used by every async HTTP, WebSocket and TFTP server drops its lock in favour of two atomic counters, which should show up as less contention on busy multi-core boxes. Around that, two smaller changes deserve a grep through your own code: TOrmTable.GetAsVariant and ToDocVariant changed their parameter from out to var, and the mORMot 1 compatibility function Lecuyer moved to another unit. Everything else is internal cleanup.

Worth a lookfix or feature you may want
Build2.4.16604 → 2.4.16618
Commits11
Lines+467 -280
Read4 min

TL;DR

  • Async server thread wake-up is now lockless: two CAS counters replace the TLightLock that guarded acoThreadSmooting.
  • TOrmTable.GetAsVariant and TOrmTable.ToDocVariant now take var instead of out — the destination variant is no longer cleared for you.
  • The legacy Lecuyer function moved from mormot.core.base to mormot.crypt.core and now returns the shared ThreadRandom generator.
  • TLoggedThread gained an ExecuteDone property, standing in for the RTL Finished property on older compilers.
  • The TFTP server compiles again on Delphi 7 and 2007 after last week's worker-lifetime fix.

Themes

The acoThreadSmooting rewrite

Two commits land the same idea: the scheduler that decides how many worker threads to wake now works on atomic counters instead of a lock. LockedGet32() is the new primitive — an atomic read-and-reset on a 32-bit value — and mormot.net.async uses it on two new counters, fWakeupOne and fWakeupEvents, in place of fThreadPollingWakeupSafe. The follow-up commit finishes the job and makes the inline directives conditional so older compilers still build.

Categories

New features2
Performance3
Compiler & platform2
Under the hood1
Tests1

New features

2

New ShortStringToVariant helper for UTF-8 short strings

core: new ShortStringToVariant() function

What changed

mormot.core.base adds ShortStringToVariant(const Txt: ShortString; var Value: variant), an inlined wrapper over RawUtf8ToVariant that produces a varString RawUtf8 variant from a UTF-8 encoded ShortString.

Impact

Additive convenience, used internally by today's ORM variant work. Nothing to change in existing code; it saves a manual conversion if you convert ShortStrings to variants yourself.

Public API

  • ShortStringToVariant(const Txt: ShortString; var Value: variant)added

TLoggedThread can now tell you its Execute has finished

core: new TLoggedThread.ExecuteDone property

What changed

mormot.core.threads adds a private fExecuteDone flag, set once the thread body returns, exposed as the read-only ExecuteDone property on TLoggedThread. The comment says it mimics the Finished property of newer Delphi and FPC runtimes.

Impact

Useful when you shut a pool of worker threads down and need to know which ones are really out of Execute, on compilers whose RTL has no Finished property. Purely additive: existing code is unaffected.

Public API

  • TLoggedThread.ExecuteDone: booleanadded — Read-only, set when Execute returns.

Performance

3

Async server thread wake-up drops its lock for atomic counters

core: new LockedGet32() wrapper function

What changed

mormot.core.base gains LockedGet32(Target: PInteger), an atomic read-and-reset built on LockedExc32. mormot.net.async then replaces the fThreadPollingWakeupSafe TLightLock with two cardinal CAS counters, fWakeupOne and fWakeupEvents, and reads them through LockedGet32 inside ThreadPollingWakeupOne and ThreadPollingWakeupEvents. About 250 lines of the wake-up path were reshuffled around those counters.

Impact

No API change for application code. On a busy async HTTP or WebSocket server with acoThreadSmooting enabled, threads no longer serialise on a lock to decide who wakes up, so wake-up latency should flatten out under contention. The effect grows with core count and connection churn; a lightly loaded server will not notice.

Public API

  • LockedGet32(Target: PInteger): integeradded — Atomic 'read then zero' on a 32-bit value, in mormot.core.base.

TOrmTable variant getters now take var instead of out

orm: optimize TOrmTable variant generation

What changed

TOrmTableAbstract.GetAsVariant and TOrmTableAbstract.ToDocVariant changed their destination parameter from 'out value: variant' to 'var value: variant', and the time-field conversion moved into a new ValueVarTimeAsText helper that fills the variant in place instead of building an intermediate value. The aim is to avoid clearing and re-allocating a variant per row when walking a result set.

Impact

Faster row-to-variant conversion on large result sets, but the parameter change is a real one: with 'out' the compiler cleared your variable before the call, with 'var' it does not. Code that passes an uninitialised local variant, or that reuses one across calls expecting it to be reset, can now see stale content or a leak. Recompiling is enough for the signature change itself; the initialisation habit is what to check.

Worth a look

Grep your codebase for GetAsVariant and ToDocVariant. Make sure the variant you pass is either freshly declared in a scope that initialises it, or explicitly VarClear-ed before the call.

Public API

  • TOrmTableAbstract.GetAsVariant(row, field: PtrInt; var value: variant; ...)signature-changed — out -> var: the destination is no longer cleared by the compiler.
  • TOrmTableAbstract.ToDocVariant(Row: PtrInt; var doc: variant; ...)signature-changed — out -> var, same caveat.

Wake-up rewrite finished, with inlining made conditional again

net: new lockless acoThreadSmooting implementation - use two CAS counters to avoid spinning on contention during thread wakeup

What changed

The finishing commit of the lockless wake-up work. ThreadPollingWakeupOne keeps its inline directive but behind {$ifdef HASINLINE}, ThreadPollingWakeupEvents loses inlining altogether, and the comment describing when a thread is woken was rewritten to mention slow REST processing next to accept() on an idle server.

Impact

No behaviour change for application code. The conditional inline matters only if you build with a compiler that has no inlining support, where the previous commit would not have compiled.

Compiler & platform

2

TFTP server builds again on Delphi 7 and 2007

net: fixed TFTP server compilation under Delphi 7/2007 - after #556 fix

What changed

The TTftpConnectionThread.HasFinished helper introduced by pull request #556 relied on the RTL Finished property, which does not exist on Delphi 7 and 2007. It was removed and replaced by the new TLoggedThread.ExecuteDone property, and the shutdown loop that terminated each worker was simplified at the same time.

Impact

If you build the TFTP server on an old Delphi, it compiles again. Everyone else gets the same behaviour through a different property. This is the compatibility follow-up to the worker-lifetime fix merged earlier today.

The legacy Lecuyer function moved out of mormot.core.base

crypt: fixed compilation in PUREMORMOT2 mode - the Lecuyer function should move to mormot.crypt.core and use the better RandomLecuyer() factory

What changed

The mORMot 1 compatibility function Lecuyer and its _Lecuyer threadvar were removed from mormot.core.base and re-declared in mormot.crypt.core, where the function now simply returns ThreadRandom. As before, the whole thing is compiled out under PUREMORMOT2. This unblocks compilation in PUREMORMOT2 mode, which the previous refactoring had broken.

Impact

Code still calling Lecuyer — typically old mORMot 1 code — now needs mormot.crypt.core in its uses clause instead of relying on mormot.core.base, and shares the ThreadRandom generator rather than its own threadvar. If you already build with PUREMORMOT2 the symbol never existed for you and nothing changes.

Worth a look

Grep for 'Lecuyer' outside the framework. Where it appears, add mormot.crypt.core to the uses clause, or switch to ThreadRandom / RandomLecuyer directly.

Public API

  • Lecuyer: PLecuyerrenamed — Same name, different unit: mormot.core.base -> mormot.crypt.core.

Under the hood

1

ORM temp variants switch to TSynVarData and standard VarClear

orm: some refactoring about variant values

What changed

Several ORM helpers that used a raw TVarData scratch value now use TSynVarData, set VType directly instead of poking through PCardinal, and release it with VarClear(variant(value)) rather than calling VarClearProc. Same code paths, clearer and less pointer-cast-heavy.

Impact

No impact on application code: these are local temporaries inside the ORM. Worth knowing only if you maintain a patch against these units, where it will conflict.

Tests

1

TFTP server test coverage extended around the #556 fix

tests: better TFTP server coverage - refactored #556 pull request

What changed

test.net.proto exercises the TFTP server more thoroughly after the worker-lifetime pull request, checking connection shutdown rather than only the happy path.

Impact

No impact on application code. It makes the regression suite more likely to catch a future break in TFTP worker shutdown.

Upgrade advice

Nothing here forces an emergency upgrade. If you run async servers under load, this batch is worth pulling for the wake-up path alone — but read the two API notes first: a var/out change and a unit move can both compile differently in your tree than they do upstream. If you build with PUREMORMOT2, the Lecuyer removal is a no-op for you.

Notes

Two merge commits (pull requests #556 and #558) carry no code of their own and have no entry. The seed edition was written by the assistant that set this repository up, using the same schema and instructions Jules follows from the next run on.