DataReel 6.0 Release Notes
DataReel 6.0 Release Notes, 10/24/2026
DataReel version 6.0
DataReel 6.0 is the first major release in many years.
Version 6.0 fixes every item listed under "Known Items Left As Is" in CODE_REVIEW_FIXES.txt. Those items were held back in the code review release because they needed a new interface or would change existing files. Every change in the source is marked with a "10/24/2026" comment. The changes were made by Claude Code, Anthropic's AI coding assistant (https://claude.ai/code).
The library version in version.txt is 6.0 and the shared library version (SHAREDLIB_VERSION in env/linux.env) is 6.0, so the Linux shared library is built as libdatareel.so.6.0. Programs must be recompiled against the 6.0 headers. Some classes gained members and some function signatures changed (see Upgrading From Earlier Versions).
How the Changes Were Tested
Linked Lists
Configuration Manager
Thread Pool
Database File Cache
Relational Database
XML Parser
RSS Feed Writer
Thread Helpers and glibc
Compiler Warnings
Other Fixes
Windows and Visual Studio 2026
One Library With SSL
Windows Schannel SSL
Upgrading From Earlier Versions
How the Changes Were Tested
- The library and the OpenSSL library build with no warnings using GCC 13 with -Wall -Wextra, at -O0 and at -O2.
- The 39 database, general and thread example programs were built with AddressSanitizer and UndefinedBehaviorSanitizer and run with no reports.
- New test programs for each change pass against 6.0. The RDBMS, XML and thread pool tests fail against the previous release, which shows that they test the problems that were fixed. The glibc mutex problem was reproduced with a small C program: after a trylock that returns ENOTRECOVERABLE, an unlock crashes and a lock never returns.
Linked Lists
- New gxList::DestroyArrayList() and gxListB::DestroyArrayList() free each item with delete[], for strings and other data allocated with new[]. DestroyList() still uses a scalar delete and is only for data allocated with a single object new.
- examples_general/doubly_linked_list uses DestroyArrayList().
Configuration Manager
- gxConfig::Add(const gxString &) hid the virtual gxListB::Add(void *) (-Woverloaded-virtual). gxConfig now overrides Add(void *) as a private function that adds nothing and returns a null value, since every node in a configuration list must hold a gxString.
- New gxConfig::Add(const char *), so Add(char_buffer) stores the text as a string instead of picking the void pointer overload.
Thread Pool
- thrPool::ClearPool() and the thrPool destructor used to delete every thread with a plain delete, even while it was still running. They now cancel running, suspended and waiting joinable threads and, under POSIX, wait for them to finish before deleting them.
- gxThread::DestroyThreadPool() and gxThread::DestroyThread() with check_state also wait for a canceled thread under POSIX. pthread_cancel() only sends a cancel request, so a canceled thread could still be running when its gxThread_t was deleted.
- New gxThreadAPIWrapper::apiStopThread() cancels a thread and waits for it.
- A canceled thread stops at its next cancellation point. A thread that never reaches one keeps these functions waiting. A canceled thread runs gxThread::ThreadCleanupHandler(), so the gxThread object must still exist when the pool is cleared. Detached threads cannot be canceled and are still deleted without being stopped.
Database File Cache
- gxDBCacheNode::Bind() loaded a segment that was not in the cache with a read length of one byte. The cache node now remembers the length of its last CachedRead(), CachedWrite() or Bind(bytes) call and loads the whole node. With no length set, only a segment that is already cached is bound, and the node address is kept.
- New gxDBCacheNode::Bind(bytes), GetNodeLength() and SetNodeLength().
- gxDatabaseCache::ReleaseSegments() now returns the segment array and leaves the cache empty. It used to set only the array pointer to null, so the head and tail still pointed into the released array. The caller frees the returned array with delete[].
Relational Database
- ALLOWDUP lookups by key: with duplicates allowed the block address is part of each B-tree key, so a search key with a block address of 0 never matched. FindRecord(), DeleteRecord() and the change functions could not find any record through an index. Find() now starts at the first key with the same CRC-32 value and reads each record with that value until one matches.
- AddRecord() no longer checks for an existing key when duplicates are allowed. The check always passed before, because the lookup always failed.
- BLOB fields in all-field keys: CreateKey() moved its write offset past a BLOB field but did not count it in the key length, so the fields after a BLOB were left out of the key. BLOB fields are now skipped without leaving a gap.
- File change: the all-field keys of tables with BLOB fields are different in 6.0. Rebuild the index files of those tables with RebuildIndex after upgrading. Index files of other tables do not change.
XML Parser
- Text mixed with child elements is kept in document order. It used to be dropped. Inside a parent, child or sub-element, each piece of text is stored as a node with an empty name and the text in its data member. Text between the parent elements of the root is stored in node_tree with the new node type gxsXML_TEXT_NODE and passed to Handle_Parent_Event() in SAX mode.
- Leading and trailing white space is removed from the text, and text that is only white space is not kept, so a document written and loaded again gives the same tree.
- The writer writes a node with no name as text.
- Code that walks a loaded tree will see the text nodes in documents with mixed content, and should skip nodes with an empty name if it only wants elements.
RSS Feed Writer
- New gxRSScloud structure with domain, port, path, registerprocedure and protocol members. gxRSSchannel::cloud is now a gxRSScloud (it was a gxString).
- The <cloud> element is written as an empty element with the five attributes the RSS 2.0 specification requires, when cloud.domain is set. It used to be written as element text, which is not valid RSS.
Thread Helpers and glibc
- In glibc 2.39 a pthread_mutex_trylock() call that returns ENOTRECOVERABLE on a robust mutex leaves the mutex in a bad state: a later unlock crashes and a later lock never returns.
- gxMutex_t has a new not_recoverable member. It is set when gxThreadMutexLock() or gxThreadMutexTryLock() gets ENOTRECOVERABLE. The lock, try lock and unlock functions then return ENOTRECOVERABLE and store gxMUTEX_NOTRECOVERABLE_ERROR without calling glibc, and gxThreadMutexDestroy() skips its unlock call. The mutex can be destroyed and initialized again.
Compiler Warnings
With -Wall -Wextra the previous library reported warnings at 117 places in the source. All are fixed:
- The private copy constructors and assignment operators used to disallow copying no longer name their unused parameter. The parameters of other functions that are not used are commented out, or marked with (void) where only WIN32 builds use them.
- "operator const int () const" is now "operator int () const", and gxDatabase::GetRevLetter() returns char.
- Casts to "const unsigned char" in htmldrv.cpp and ostrbase.cpp are now casts to unsigned char.
- HyperText::TableHeader() and TableData() take a const char *valign, and PostScriptDrv::SetDocumentName() keeps a const char pointer, so string literals can be passed without a cast.
- Signed and unsigned compares in gxdatagm.cpp, gxstream.cpp, scomserv.cpp and gxrdbhdr.cpp.
- gxBStree::Find() had an unused variable.
Other Fixes
Found while testing the changes above:
- DiskFileB::df_copy() stored the file lengths in unsigned variables, so the tests for a futils_filelength() error could never be true.
- gxDatabase::ReadObjectChecksum() compared CRC values that were never set when the file was not open. It now returns 0. The optional CRC pointers are checked for null on every path.
- ASPrint() for int, long and double values formatted the number into a buffer the size of the field width, which overflowed for a narrow field. long values were truncated to int. A fixed buffer and snprintf() are used now.
- operator>>() for UString and UStringw read an uninitialized buffer when the input failed, for example at the end of the input. Found by AddressSanitizer in examples_general/string_class.
Windows and Visual Studio 2026
The nmake build files and the Windows code were reviewed and updated for Visual Studio 2026 (MSVC 14.50). VS 2022 is still supported.
Build files:
- winslib/msvc.env used the cdebug, cflags, cvarsmt, guilflags and guilibsmt macros from win32.mak, which is no longer part of the Windows SDK. Those macros were empty, so the static library and every program linked to it were built with no optimization in release builds and no debug information in debug builds. The flags are now set in msvc.env: /MT /O2 for release builds and /MTd /Z7 /Od for debug builds, with /W3 /EHsc /GR for both.
- The Windows system libraries are now listed in msvc.env. The OpenSSL builds also link ws2_32.lib, which the OpenSSL 3.x static libraries need.
- env/msvc.mak and env/msvc_dll.mak passed LINK_FLAGS, which was never set, to the linker. They now pass LINKER_FLAGS. winslib/project.mak passes /nologo to lib.exe.
- dll/msvc.env linked with /machine:I386, so the DLL could not be built with the x64 compiler. The linker now takes the target CPU from the object files. The /pdbtype:sept option, which the linker no longer supports, was removed.
- _CRT_SECURE_NO_WARNINGS, _CRT_NONSTDC_NO_WARNINGS and _WINSOCK_DEPRECATED_NO_WARNINGS are defined in MSVC_DEFMACS, so the C4996 deprecation warnings for the C string, POSIX and WinSock 1.1 functions do not hide other warnings. Comment out MSVC_DEFMACS to see them. _HAS_STD_BYTE=0 is also defined, so std::byte cannot conflict with the Windows byte type if the code is built with /std:c++17 or later.
ENV BAT files:
- New env/vs2026_vars.bat finds the VS 2026 install with vswhere.exe and calls vcvarsall.bat. It works with the Community, Professional, Enterprise, Build Tools and Insiders editions. The other BAT files called the Insiders path directly, which does not exist in a release install of VS 2026.
- The default Windows build is now 64-bit x64. env/msvc.bat and env/msvc64_vs2026.bat set up x64 builds, and env/msvc_vs2022.bat sets up x64 builds with VS 2022. env/msvc64_vs2026.bat called vcvarsamd64_x86.bat before, which builds 32-bit programs on a 64-bit host. For 32-bit x86 builds of legacy code use env/msvc32_vs2026.bat.
- The OpenSSL ENV files point to the openssl64 install by default.
Source:
- gxdtyp64.h used the old _int64 keyword. It now uses __int64.
- gxRandom::entropy_number() in gxssl.cpp and the WIN32 thread entry in thrapiw.cpp cast pointers to unsigned long and DWORD, which are 32 bits in 64-bit Windows builds. They cast through size_t now. The detach, priority, stack and thread_pool thread examples had the same cast.
- The futils directory functions and futils_GetDiskSpace() use the ANSI Windows functions (FindFirstFileA, FindNextFileA, GetDiskFreeSpaceA and GetDiskFreeSpaceExA). In _UNICODE builds the __VS2026__ code copied a wide file name with strcpy(), which kept only the first letter, and passed a wide string to GetDiskFreeSpaceExA. futils_readdir() did not set d_namlen in ANSI builds.
- futils_filelength() called CreateFile(TEXT(fname)), which does not compile with _UNICODE when the 64-bit integer option is used. It calls CreateFileA() now.
- futils_mode() discarded the result of tolower(), so files ending in .EXE or .COM in upper case were not marked as programs.
- gxdlcode.h defines __VS2026__ when _MSC_VER is 1950 (VS 2026) or higher, so the VS 2026 _UNICODE code is used without a -D__VS2026__ flag.
How these changes were tested:
- The library, the DLL, the utils and the examples were cross-compiled on Linux with the MinGW-w64 GCC 13 compilers for 32-bit and 64-bit Windows, with the MSVC defines from msvc.env, in ANSI and _UNICODE builds. All 64 programs link. The OpenSSL examples were not built.
- The 64-bit programs were run under Wine 9. The database, general and thread examples ran, the directory functions were checked in ANSI and _UNICODE builds, and the simple socket client and server exchanged data.
- The library was not built with the Microsoft compiler or nmake for this release. Build it with VS 2026 on Windows before you use it.
One Library With SSL
- SSL is built into the library on every platform. There is no separate SSL library.
- Linux: "make" in the build directory builds libdatareel with OpenSSL. env/linux.env sets USE_OPEN_SSL to YES unless it is already set, and the example programs link with -lssl -lcrypto. The libdatareel_ssl library and build/makefile_ssl were removed. On a system without OpenSSL, build libdatareel and your programs with "make USE_OPEN_SSL=NO".
- Windows: the gxcode library uses the native Windows Schannel (SSPI) TLS API by default, so OpenSSL does not need to be built. Programs link with secur32.lib, crypt32.lib and ncrypt.lib, which come with the Windows SDK. msvc.env adds -D__USE_GX_SSL_EXTENSIONS__ and -D__USE_GX_SCHANNEL__. OpenSSL is still an option: build the library with msvc_ssl.mak and your programs with USESSL=1.
Windows Schannel SSL
- New file src/gxsschan.cpp has the Schannel version of the gxSSL class. gxssl.cpp includes it when __USE_GX_SCHANNEL__ is defined. The gxSSL functions, error codes and messages are the same for both versions.
- Client side: TLS 1.2 and TLS 1.3 (where Windows supports it), SNI, and VerifyCert() with the Windows certificate chain engine. With SetCAList() the chain must end at a CA in the CA file. Without it the Windows trusted root store is used.
- Server side: InitCTX() loads PEM or DER cert and key files. RSA (PKCS#1 and PKCS#8) and EC keys are supported, and the key is imported into CNG for the life of the context. A .pfx or .p12 file with the key password also works. Encrypted PEM keys need a .pfx file.
- GetCTX() and GetSSL() return Schannel objects, so programs that call OpenSSL functions directly must use the OpenSSL build.
- MakeDHParms(), MakeRSAPrivateKey() and MakeCertReq() need OpenSSL and return an error in the Schannel build. LoadDHParms(), InitPRNG() and MakeRSAEphKey() do nothing because Schannel handles key exchange.
- New gxSSL functions for both versions: SetHostName() for SNI, GetPeerCertInfo() for the peer's subject and issuer, and CertVerifyErrorString() for VerifyCert() return codes. gxsHTTPClient sets the host name, so HTTPS requests to virtual hosted sites work with both versions.
- The ssl_verify_cert example uses the new functions and works with both versions. The https_client, http_client, ssl_server and ssl_verify_cert msvc.mak files no longer set USESSL=1.
- Tested by cross-compiling with MinGW-w64 for x86 and x64 and running under Wine 9: the client against openssl s_server, cert checks with a good and a bad host name, the server with a .pfx file against a Python ssl client, a 1 MB transfer, a read timeout, and the https_client example. Wine cannot import PEM keys into CNG, so the PEM server path and TLS 1.3 still need a test on Windows.
- Tested on Windows 11 (10.0.26200) with VS 2026 (cl 19.50) x64: the library, the DLL and all 61 examples and utils build with nmake. Cert verify, https_client and the ssl_server example (PEM key) pass with TLS 1.2 and TLS 1.3.
- MakeCertReq() signed certs with MD5, which Schannel rejects. It signs with SHA-256 now, and the ssl_server example cert was re-signed with SHA-256.
- The debug DLL link no longer uses /incremental:yes (LNK4224).
Upgrading From Earlier Versions
- Recompile every program against the 6.0 headers and link with the 6.0 library.
- gxRSSchannel::cloud is a gxRSScloud. Set cloud.domain, cloud.port, cloud.path, cloud.registerprocedure and cloud.protocol instead of assigning a string.
- gxDatabaseCache::ReleaseSegments() returns the segment array. Free it with delete[] when it is no longer used.
- gxConfig objects no longer accept untyped data through gxListB::Add(void *).
- Rebuild the index files of RDBMS tables that have BLOB fields and an all-field key ("PRI: COL NONE").
- Programs that walk gxsXML trees loaded from documents with mixed content should skip nodes with an empty name, or handle them as text.
- A thread pool is now cleared by stopping its running joinable threads. Threads must reach a cancellation point, and the gxThread object must still exist when the pool is cleared or deleted.
DataReel Copyright (c) 2001-2026 DataReel Software Development