63 #include <mach-o/dyld.h>
64#elif !defined(WINDOWS)
77 ConcRand(uint64 seed) :
s(seed ? seed : 0x9E3779B97F4A7C15ULL) {}
79 uint32
below(uint32 n) {
return n ? (uint32)(
next() % n) : 0; }
84 const char* v = getenv(name);
85 if (!v || !*v)
return def;
87 return (n > 0) ? (uint32)n : def;
90 const char* v = getenv(name);
91 return v && *v ==
'1';
96 uint32 h = 2166136261u;
97 for (uint32 i = 0; i < len; i++) { h ^= (uint8)p[i]; h *= 16777619u; }
101 for (uint32 i = 0; i < len; i++)
102 buf[i] = (
char)(
'A' + ((serial * 31 + i * 7) % 26));
109 char* body =
new char[len + 1];
114 m->
setInt(
"cfserial", (int64)serial);
115 m->
setInt(
"cflen", (int64)len);
116 m->
setInt(
"cfsum", (int64)sumOut);
147 int64 ser = m->
getInt(
"cfserial", ok);
148 if (!ok) {
torn++;
mutex.leave();
return; }
149 int64 len = m->
getInt(
"cflen");
150 int64 sum = m->
getInt(
"cfsum");
152 uint32 actual =
ConcChecksum(body.c_str(), (uint32)body.size());
153 if ((int64)body.size() != len || actual != (uint32)sum)
162 const char*
reconcile(
char* det,
size_t detSize,
bool lostInject) {
164 uint32 dup = 0, lost = 0, extra = 0;
165 uint32 firstDup = 0, firstLost = 0;
166 std::map<uint32, uint32>::const_iterator it;
168 std::map<uint32, uint32>::const_iterator r =
readCount.find(it->first);
169 uint32 n = (r ==
readCount.end()) ? 0 : r->second;
171 if (lostInject && it->first == 1) n = 0;
172 if (n == 0) { lost++;
if (!firstLost) firstLost = it->first; }
173 else if (n > 1) { dup++;
if (!firstDup) firstDup = it->first; }
180 snprintf(det, detSize,
181 "written=%u distinct-read=%u LOST=%u DUPLICATED=%u never-written=%u "
182 "corrupt=%u torn=%u (first lost=%u first dup=%u)",
183 w, (uint32)
readCount.size(), lost, dup, extra, c, t, firstLost, firstDup);
184 if (t)
return "TORN";
185 if (c)
return "CORRUPT";
186 if (extra)
return "READ_BUT_NEVER_WRITTEN";
187 if (dup)
return "DUPLICATED";
188 if (lost)
return "LOST";
217 while (serial <= c->totalMsgs && !c->
stop) {
223 for (uint32 i = 0; i < n && serial <= c->
totalMsgs && !c->
stop; i++) {
227 else len = 1 + rng.
below(300);
246 for (uint32 g = 0; g < 50 && !c->
stop; g++) {
258 uint32 idleRounds = 0;
293 uint32 serial = 0xF0000000u + (c->
readsSeen & 0xFFFFF);
316 uint32 payload, uint32 burst, uint64 seed,
bool resizeStress,
317 bool blockingReads,
bool dupSelftest,
bool lostSelftest) {
319 if (!manager->
create(0)) {
327 memset(&ctx, 0,
sizeof(ctx));
343 std::vector<uint32> tids;
345 for (uint32 i = 0; i < readers; i++) {
347 unittest::fail(
"%s: could not create reader thread %u", label, i);
372 bool anyAlive =
false;
373 for (
size_t i = 0; i < tids.size(); i++)
375 if (!anyAlive)
break;
379 for (uint32 g = 0; g < 200; g++) {
380 bool anyAlive =
false;
381 for (
size_t i = 0; i < tids.size(); i++)
383 if (!anyAlive)
break;
388 const char* cls = oracle.
reconcile(det,
sizeof(det), lostSelftest);
428 const char* tmpEnv = getenv(
"TEMP");
429 if (!tmpEnv || !*tmpEnv) tmpEnv = getenv(
"TMP");
430 std::string childList = std::string((tmpEnv && *tmpEnv) ? tmpEnv :
".")
431 +
"\\cmsdk_concfuzz_child_reads.txt";
433 std::string childList =
"/tmp/cmsdk_concfuzz_child_reads.txt";
438 if (!manager->
create(0)) {
452 const char* exeEnv = getenv(
"PSY_CONC_EXE");
453 if (exeEnv && *exeEnv)
458 DWORD n = GetModuleFileNameA(NULL, buf,
sizeof(buf));
459 if (n > 0 && n <
sizeof(buf)) { buf[n] = 0; exe = buf; }
460#elif defined(__APPLE__)
462 uint32_t sz =
sizeof(buf);
463 if (_NSGetExecutablePath(buf, &sz) == 0)
467 ssize_t n = readlink(
"/proc/self/exe", buf,
sizeof(buf) - 1);
468 if (n > 0) { buf[n] = 0; exe = buf; }
472 unittest::fail(
"TIER B: cannot determine own executable path for re-exec "
473 "(set PSY_CONC_EXE=<path to CMSDKTest> to override)");
482 _putenv_s(
"PSY_CONC_CHILD",
"read");
483 _putenv_s(
"PSY_CONC_CHILD_OUT", childList.c_str());
486 setenv(
"PSY_CONC_CHILD",
"read", 1);
487 setenv(
"PSY_CONC_CHILD_OUT", childList.c_str(), 1);
496 unittest::fail(
"TIER B: could not spawn reader child (%s)", cmd.c_str());
506 std::map<uint32, uint32> written;
507 for (uint32 serial = 1; serial <= msgs; serial++) {
508 uint32 len = payload ? payload : (1 + rng.
below(300));
512 written[serial] = sum;
514 if ((serial % 64) == 0)
528 unittest::detail(
"TIER B: child did not exit in time; terminated (partial result)");
534 std::map<uint32, uint32> readCount;
536 for (
size_t i = 0; i < lines.size(); i++) {
537 if (lines[i].find(
"R|") != 0)
continue;
539 if (f.size() < 3)
continue;
540 uint32 s = (uint32)strtoul(f[1].c_str(), NULL, 10);
541 uint32 c = (uint32)strtoul(f[2].c_str(), NULL, 10);
543 std::map<uint32, uint32>::const_iterator w = written.find(s);
544 if (w != written.end() && w->second != c) badSum++;
546 uint32 lost = 0, dup = 0, extra = 0, firstLost = 0;
547 for (std::map<uint32, uint32>::const_iterator it = written.begin(); it != written.end(); ++it) {
548 std::map<uint32, uint32>::const_iterator r = readCount.find(it->first);
549 uint32 n = (r == readCount.end()) ? 0 : r->second;
550 if (n == 0) { lost++;
if (!firstLost) firstLost = it->first; }
551 else if (n > 1) dup++;
553 for (std::map<uint32, uint32>::const_iterator it = readCount.begin(); it != readCount.end(); ++it)
554 if (written.find(it->first) == written.end()) extra++;
560 unittest::fail(
"TIER B: child produced NO read records (%s) - the cross-process "
561 "path is UNMEASURED, not clean. child exited=%d rc=%d",
562 childList.c_str(), (
int)exited, (
int)rc);
565 if (lost || dup || extra || badSum) {
566 unittest::fail(
"TIER B (cross-process): written=%u child-read-distinct=%u LOST=%u "
567 "DUPLICATED=%u never-written=%u badChecksum=%u (first lost=%u)",
568 (uint32)written.size(), (uint32)readCount.size(), lost, dup, extra, badSum,
572 unittest::detail(
"TIER B (cross-process): OK :: written=%u child-read-distinct=%u "
573 "LOST=0 DUPLICATED=0 badChecksum=0 (real cross-process mutex + two header mappings)",
574 (uint32)written.size(), (uint32)readCount.size());
581 const char* outPath = getenv(
"PSY_CONC_CHILD_OUT");
582 uint32 want =
ConcEnvU32(
"PSY_CONC_CHILD_MSGS", 20000);
583 if (!outPath || !*outPath)
589 if (!manager->
connect(0,
false)) {
593 uint32 got = 0, idle = 0;
595 while (got < want && idle < 600) {
597 if (!m) { idle++;
continue; }
600 int64 ser = m->
getInt(
"cfserial", ok);
602 uint32 sum =
ConcChecksum(body.c_str(), (uint32)body.size());
609 if (batch.size() > 8192) {
623 const char* childMode = getenv(
"PSY_CONC_CHILD");
624 if (childMode && *childMode) {
631 const char* tierEnv = getenv(
"PSY_CONC_TIER");
632 std::string tier = (tierEnv && *tierEnv) ? tierEnv :
"ALL";
633 uint32 readers =
ConcEnvU32(
"PSY_CONC_READERS", 4);
634 uint32 msgs =
ConcEnvU32(
"PSY_CONC_MSGS", 20000);
635 uint32 burst =
ConcEnvU32(
"PSY_CONC_BURST", 64);
636 uint64 seed = (uint64)
ConcEnvU32(
"PSY_CONC_SEED", 0x2000);
638 bool lostSt =
ConcEnvFlag(
"PSY_CONC_LOSTSELFTEST");
642 uint32 payload =
ConcEnvU32(
"PSY_CONC_PAYLOAD", 2280);
644 bool all = (tier ==
"ALL");
647 if (ok && (all || tier ==
"A")) {
650 0 , seed,
false,
false, dupSt, lostSt);
652 if (ok && (all || tier ==
"A2")) {
655 burst, seed,
false,
false, dupSt, lostSt);
657 if (ok && (all || tier ==
"B")) {
661 if (ok && (all || tier ==
"C")) {
663 ok =
ConcRunThreaded(
"TIER C (resize-under-read)", readers, msgs, payload,
664 0, seed,
true ,
false, dupSt, lostSt);
666 if (ok && (all || tier ==
"D")) {
668 ok =
ConcRunThreaded(
"TIER D (blocking reads)", readers, msgs, payload,
669 burst, seed,
false,
true , dupSt, lostSt);
Central shared-memory manager for a Psyclone node: master segment, per-subsystem shared maps and the ...
Shared-memory process ("space") table plus per-process message queues.
Process-wide thread registry and lifecycle manager: the concurrency core of CMSDK.
Small, dependency-free unit test harness used by all CMSDK object tests.
#define THREAD_FUNCTION_CALL
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
std::string getAsString(const char *key)
getAsString(const char* key)
bool isValid()
isValid() Checks that the message memory block exists and carries the current-format object id (DATAM...
bool setSerial(uint64 serial)
setSerial(uint64 serial)
Top-level facade of the shared-memory subsystem for one process.
ProcessMemory * processMemory
Accessor for the process table and per-process queues.
bool create(uint16 sysID, uint32 slotCount=100000, uint16 binCount=2, uint32 minBlockSize=1024, uint32 maxBlockSize=64 *1024, uint64 initSize=50000000L, uint64 maxSize=1000000000L, bool force=false)
Create all shared segments for a new node instance (master process only).
bool connect(uint16 sysID, bool isMaster)
Attach this process to an existing node's shared segments.
bool addToMsgQ(uint16 procID, DataMessage *msg)
Enqueue on the data-message queue.
uint32 getMsgQCount(uint16 procID)
DataMessage * waitForMsgQ(uint16 procID, uint32 timeout)
Wait on the data-message queue.
static bool ConcFuzzTest()
Phase 2: CONCURRENT + cross-process fuzz of the ring (ProcessMemoryConcFuzz.cpp).
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
static bool IsThreadRunning(uint32 id)
Check whether the thread is still alive at the OS level.
Recursive mutual-exclusion lock, optionally named for cross-process use.
bool leave()
Release the mutex.
bool enter()
Block until the mutex is acquired.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
uint32 NewProcess(const char *cmdline, const char *initdir=NULL, const char *title=NULL, int16 x=-1, int16 y=-1, int16 w=-1, int16 h=-1)
Launch a detached child process.
bool EndProcess(uint32 proc)
Forcibly terminate a child process.
bool Sleep(uint32 ms)
Suspend the calling thread.
uint8 GetProcessStatus(uint32 proc, int &returncode)
Poll a child started with NewProcess().
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
std::string ReadAFileString(std::string filename)
Read an entire file into a std::string.
bool AppendToAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Append to a file, creating it if missing.
std::vector< std::string > TextListSplit(const char *text, const char *split, bool keepEmpty=true, bool autoTrim=false)
Split text on a separator.
bool DeleteAFile(const char *filename, bool force)
Delete a file.
std::vector< std::string > TextListSplitLines(const char *text, bool keepEmpty=true, bool autoTrim=false)
Split text into lines, handling both \n and \r\n.
std::string StringFormat(const char *format,...)
printf into a std::string.
static uint32 ConcChecksum(const char *p, uint32 len)
FNV-1a — identical to Phase 1's so payload checks are comparable.
static THREAD_RET THREAD_FUNCTION_CALL ConcReader(THREAD_ARG arg)
static bool ConcRunCrossProcess(uint32 msgs, uint32 payload, uint64 seed)
Tier B: two OS processes on one segment.
static bool ConcCrossProcessChild()
Tier B child half: open() the existing segment and drain, appending every serial read.
static THREAD_RET THREAD_FUNCTION_CALL ConcWriter(THREAD_ARG arg)
static THREAD_RET THREAD_FUNCTION_CALL ConcResizer(THREAD_ARG arg)
Tier C helper: grow the segment repeatedly while readers are live, so resize() unmaps and repoints he...
static DataMessage * ConcMakeMsg(uint32 serial, uint32 len, uint32 &sumOut)
Build one message carrying its own serial + payload, so a reader can re-derive what it SHOULD have re...
static bool ConcEnvFlag(const char *name)
static uint32 ConcEnvU32(const char *name, uint32 def)
static void ConcFillPayload(char *buf, uint32 len, uint32 serial)
static bool ConcRunThreaded(const char *label, uint32 readers, uint32 msgs, uint32 payload, uint32 burst, uint64 seed, bool resizeStress, bool blockingReads, bool dupSelftest, bool lostSelftest)
Run one in-process tier.
volatile uint32 readsSeen
std::map< uint32, uint32 > readCount
void noteRead(DataMessage *m, bool dupInject)
Record one read.
std::map< uint32, uint32 > expectSum
const char * reconcile(char *det, size_t detSize, bool lostInject)
void noteWritten(uint32 serial, uint32 sum)
Deterministic xorshift64* — same as Phase 1 so seeds behave comparably.