diff --git a/.gitignore b/.gitignore index 0a4a118..7e8567a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,335 @@ +*.exe +out/ + + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ -*.out diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..901698f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,40 @@ +{ + "configurations": [ + { + "name": "Win32", + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "compilerPath": "D:\\PROD\\programs\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.26.28801\\bin\\Hostx64\\x64\\cl.exe", + "cStandard": "c11", + "cppStandard": "c++17", + "windowsSdkVersion": "10.0.18362.0", + "includePath": [ + "D:\\PROD\\programs\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.26.28801\\include", + "D:\\Windows Kits\\10\\Include\\10.0.18362.0\\ucrt", + "D:\\Windows Kits\\10\\Include\\10.0.18362.0\\um", + "D:\\Windows Kits\\10\\Include\\10.0.18362.0\\shared" + ] + }, + { + "name": "CLANG", + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "compilerPath": "D:\\PROD\\programs\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\Llvm\\bin\\clang.exe", + "cStandard": "c11", + "cppStandard": "c++17", + "windowsSdkVersion": "10.0.18362.0", + "includePath": [ + "D:\\PROD\\programs\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\Llvm\\lib\\clang\\10.0.0\\include" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d8af2b8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "win Launch", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/sample/win/windows_sample_dbg.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/sample/win", + "environment": [], + "externalConsole": false + }, + { + "name": "cpp Launch", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/sample/cpp/cppsample_dbg.exe", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}/sample/cpp", + "environment": [], + "externalConsole": false + } + ] +} \ No newline at end of file diff --git a/.vscode/readme.md b/.vscode/readme.md new file mode 100644 index 0000000..197ec9e --- /dev/null +++ b/.vscode/readme.md @@ -0,0 +1,29 @@ +# Notes about VS Code C++ setup + +AFAIK these are the notes missing from +[VS Code MSVC setup page](https://code.visualstudio.com/docs/cpp/config-msvc). + +- `tasks.json` and `launch.json` are **not** connected + - when you do "CTRL+SHIFT+B" `tasks.json` is used. It defines how you want you executable to be built. + - when you start debugger `launch.json` is used + - this is Code "out of the box" behavior. +- `c_cpp_properties` has to be re-checked on each C++ tools update. Or SDK update. + - this is especially true if you use Visual Studio on the same machine + - if you want "pure" VS Code C++ experience use it on machine which has no Visual Studio installed + - this means "[Build Tools for Visual Studio](https://visualstudio.microsoft.com/downloads/#other)" + + +**clang on Windows** + +I have not had time yet to resolve this one. + +## Caveat Emptor + +If you use WIN10 as a C/C++ development machine use Visual Studio Community Eddition. + +If you like pain use VS Code. + +It is as simple as that. + +--- +(c) 2020 by dbj@dbj.org CC BY SA 4.0 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6598232 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,79 @@ +{ + // Command Prompt + // "terminal.integrated.shell.windows": "C:\\Windows\\System32\\cmd.exe" + // PowerShell + // "terminal.integrated.shell.windows": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + // Git Bash + // "terminal.integrated.shell.windows": "C:/Program Files/Git/git-bash.exe", + // "terminal.integrated.shell.windows": "C:/Program Files/Git/git-bash.exe", + // Bash on Ubuntu (on Windows) + // "terminal.integrated.shell.windows": "C:\\Windows\\System32\\bash.exe" + "terminal.integrated.rightClickBehavior": "selectWord", + "terminal.integrated.rendererType": "dom", + "terminal.integrated.fontFamily": "consolas", + "files.associations": { + "iostream": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "cmath": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "map": "cpp", + "string": "cpp", + "xutility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "memory": "cpp", + "new": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "utility": "cpp", + "xfacet": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocinfo": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "condition_variable": "cpp", + "list": "cpp", + "resumable": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "functional": "cpp", + "future": "cpp", + "mutex": "cpp", + "optional": "cpp", + "xhash": "cpp", + "coroutine": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e885b21 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,74 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "CLANG C win32 debug", + "type": "shell", + "command": "D:\\PROD\\programs\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\Llvm\\bin\\clang.exe", + "args": [ + "-D_HAS_EXCEPTIONS=0", + "-g", + "${workspaceFolder}/sample/win/windows_sample.c", + "${workspaceFolder}/lfqueue.c", + "-o", + "${workspaceFolder}/sample/win/windows_sample.exe" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$gcc" + }, + { + "label": "C win32 debug", + "type": "shell", + "command": "cl.exe", + "args": [ + "/MDd", + "/kernel", + "-D_HAS_EXCEPTIONS=0", + "/Fo${workspaceFolder}\\sample\\win\\", + "/Zi", + "/Fe:", + "${workspaceFolder}/sample/win/windows_sample_dbg.exe", + "${workspaceFolder}/sample/win/windows_sample.c", + "${workspaceFolder}/lfqueue.c" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "C win32 release", + "type": "shell", + "command": "cl.exe", + "args": [ + "/MD", + "/kernel", + "-D_HAS_EXCEPTIONS=0", + "/Fo${workspaceFolder}\\sample\\win\\", + "/O2", + "/Fe:", + "${workspaceFolder}/sample/win/windows_sample.exe", + "${workspaceFolder}/sample/win/windows_sample.c", + "${workspaceFolder}/lfqueue.c" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 9fe89fd..1463887 100644 --- a/README.md +++ b/README.md @@ -1,109 +1,7 @@ -# lfqueue [![Build Status](https://travis-ci.org/Taymindis/lfqueue.svg?branch=master)](https://travis-ci.org/Taymindis/lfqueue) -lock-free FIFO queue by C native built it, easy built cross platform(no extra dependencies needed) , guarantee thread safety memory management ever! +#### [Interesting code](https://github.com/dbj-data/lfqueue/tree/master/sample) not in the original. +## Caveat Emptor -# All Platform tests - -GCC/CLANG | [![Build Status](https://travis-ci.org/Taymindis/lfqueue.svg?branch=master)](https://travis-ci.org/Taymindis/lfqueue) - -VS x64/x86 | [![Build status](https://ci.appveyor.com/api/projects/status/7srsrdgj7f524sam?svg=true)](https://ci.appveyor.com/project/Taymindis/lfqueue) - - -## API -```c - -extern int lfqueue_init(lfqueue_t *lfqueue); -extern int lfqueue_enq(lfqueue_t *lfqueue, void *value); -extern void* lfqueue_deq(lfqueue_t *lfqueue); -extern void* lfqueue_single_deq(lfqueue_t *lfqueue); -extern void lfqueue_destroy(lfqueue_t *lfqueue); -extern size_t lfqueue_size(lfqueue_t *lfqueue); -extern void lfqueue_sleep(unsigned int milisec); - -``` - - -## Example - -```c - -int* int_data; -lfqueue_t my_queue; - -if (lfqueue_init(&my_queue) == -1) - return -1; - -/** Wrap This scope in other threads **/ -int_data = (int*) malloc(sizeof(int)); -assert(int_data != NULL); -*int_data = i++; -/*Enqueue*/ - while (lfqueue_enq(&my_queue, int_data) == -1) { - printf("ENQ Full ?\n"); -} - -/** Wrap This scope in other threads **/ -/*Dequeue*/ -while ( (int_data = lfqueue_deq(&my_queue)) == NULL) { - printf("DEQ EMPTY ..\n"); -} - -// printf("%d\n", *(int*) int_data ); -free(int_data); -/** End **/ - -lfqueue_destroy(&my_queue); - -``` - - -#### If you are using single thread dequeue/consume. Please use `lfqueue_single_deq` to get better result - - -## Build and Installation - -For linux OS, you may use cmake build, for other platforms, please kindly include the source code and header file into the project, e.g. VS2017, DEV-C++, Xcode - -```bash -mkdir build - -cd build - -cmake .. - -make - -./lfqueue-example - -valgrind --tool=memcheck --leak-check=full ./lfqueue-example - -sudo make install - - -``` - -## continuously Test - -For continuously test until N number, if you having any issue while testing, please kindly raise an issue - -```bash - -./keep-testing.sh - -``` - - -## Uninstallation - -```bash -cd build - -sudo make uninstall - -``` - - -## You may also like lock free stack LIFO - -[lfstack](https://github.com/Taymindis/lfstack) \ No newline at end of file +Original author: [Taymindis](https://github.com/Taymindis/) +Please repsect the license at origin diff --git a/example_wins.c b/example_wins.c deleted file mode 100644 index 8f63ed1..0000000 --- a/example_wins.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include -#include -#include -#include "lfqueue.h" -#include -#include - -struct timeval tv1, tv2; -lfqueue_t myq; - -#define nthreads 8 -#define total_put 50000 - -unsigned __stdcall worker(void *); -unsigned __stdcall worker(void *arg) -{ - int i = 0; - int *int_data; - while (i < total_put) { - int_data = (int*)malloc(sizeof(int)); - assert(int_data != NULL); - *int_data = i++; - /*Enqueue*/ - - while (lfqueue_enq(&myq, int_data)) { - printf("ENQ FULL?\n"); - } - - /*Dequeue*/ - while ((int_data = lfqueue_deq(&myq)) == NULL) { - // usleep(1000); - printf("DEQ EMPTY?\n"); - } - free(int_data); - } - return 0; -} - -#define join_threads \ -for (i = 0; i < nthreads; i++)\ -WaitForSingleObject(threads[i], INFINITE) -/* -#define detach_thread_and_loop \ -for (i = 0; i < nthreads; i++)\ -pthread_detach(threads[i]);\ -while (1) {\ -sleep(2);\ -printf("current size= %zu\n", lfqueue_size(&myq) );\ -}*/ - - -int main(void) -{ - //const static int nthreads = 2;//sysconf(_SC_NPROCESSORS_ONLN); // Linux - int i, n; - if (lfqueue_init(&myq) == -1) - return -1; - - for (n = 0; n < 100; n++) { - /* Spawn threads. */ - printf("Current running at %d, Total threads = %d\n", n, nthreads); - clock_t start = clock(); - HANDLE threads[nthreads]; - - for (i = 0; i < nthreads; i++) { - unsigned udpthreadid; - threads[i] = (HANDLE)_beginthreadex(NULL, 0, worker, NULL, 0, &udpthreadid); - } - - join_threads; - // detach_thread_and_loop; - - clock_t end = clock(); - - printf("Total time = %f seconds\n", (float)(end - start) / CLOCKS_PER_SEC); - - assert(0 == lfqueue_size(&myq) && "Error, all queue should be consumed but not"); - - } - - lfqueue_destroy(&myq); - printf("Test Pass!\n"); - //getchar(); - return 0; -} - diff --git a/lfqueue.c b/lfqueue.c index fb0686c..b9260c4 100644 --- a/lfqueue.c +++ b/lfqueue.c @@ -27,6 +27,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + +#include // DBJ ADDED #include #include #include @@ -43,51 +45,62 @@ #define __LFQ_YIELD_THREAD sched_yield #define __LFQ_SYNC_MEMORY __sync_synchronize -#else +#else /* NOT defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ */ +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX #include + #include #ifdef _WIN64 -inline BOOL __SYNC_BOOL_CAS(LONG64 volatile *dest, LONG64 input, LONG64 comparand) { +inline BOOL __SYNC_BOOL_CAS(LONG64 volatile *dest, LONG64 input, LONG64 comparand) +{ return InterlockedCompareExchangeNoFence64(dest, input, comparand) == comparand; } #define __LFQ_VAL_COMPARE_AND_SWAP(dest, comparand, input) \ - InterlockedCompareExchangeNoFence64((LONG64 volatile *)dest, (LONG64)input, (LONG64)comparand) + InterlockedCompareExchangeNoFence64((LONG64 volatile *)dest, (LONG64)input, (LONG64)comparand) #define __LFQ_BOOL_COMPARE_AND_SWAP(dest, comparand, input) \ - __SYNC_BOOL_CAS((LONG64 volatile *)dest, (LONG64)input, (LONG64)comparand) + __SYNC_BOOL_CAS((LONG64 volatile *)dest, (LONG64)input, (LONG64)comparand) #define __LFQ_FETCH_AND_ADD InterlockedExchangeAddNoFence64 #define __LFQ_ADD_AND_FETCH InterlockedAddNoFence64 #define __LFQ_SYNC_MEMORY MemoryBarrier -#else +#else /* NOT _WIN64*/ + #ifndef asm #define asm __asm #endif -inline BOOL __SYNC_BOOL_CAS(LONG volatile *dest, LONG input, LONG comparand) { +inline BOOL __SYNC_BOOL_CAS(LONG volatile *dest, LONG input, LONG comparand) +{ return InterlockedCompareExchangeNoFence(dest, input, comparand) == comparand; } #define __LFQ_VAL_COMPARE_AND_SWAP(dest, comparand, input) \ - InterlockedCompareExchangeNoFence((LONG volatile *)dest, (LONG)input, (LONG)comparand) + InterlockedCompareExchangeNoFence((LONG volatile *)dest, (LONG)input, (LONG)comparand) #define __LFQ_BOOL_COMPARE_AND_SWAP(dest, comparand, input) \ - __SYNC_BOOL_CAS((LONG volatile *)dest, (LONG)input, (LONG)comparand) + __SYNC_BOOL_CAS((LONG volatile *)dest, (LONG)input, (LONG)comparand) #define __LFQ_FETCH_AND_ADD InterlockedExchangeAddNoFence #define __LFQ_ADD_AND_FETCH InterlockedAddNoFence #define __LFQ_SYNC_MEMORY() asm mfence -#endif -#include +#endif /* NOT _WIN64 */ + +// #include #define __LFQ_YIELD_THREAD SwitchToThread -#endif + +#endif /* defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ */ #include "lfqueue.h" #define DEF_LFQ_ASSIGNED_SPIN 2048 +/*----------------------------------------------------------------------------------------------*/ + #if defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ #define lfq_time_t long #define lfq_get_curr_time(_time_sec) \ -struct timeval _time_; \ -gettimeofday(&_time_, NULL); \ -*_time_sec = _time_.tv_sec + struct timeval _time_; \ + gettimeofday(&_time_, NULL); \ + *_time_sec = _time_.tv_sec #define lfq_diff_time(_etime_, _stime_) _etime_ - _stime_ #else #define lfq_time_t time_t @@ -95,47 +108,85 @@ gettimeofday(&_time_, NULL); \ #define lfq_diff_time(_etime_, _stime_) difftime(_etime_, _stime_) #endif -struct lfqueue_cas_node_s { - void * value; +/*----------------------------------------------------------------------------------------------*/ + +struct lfqueue_cas_node_s +{ + void *value; struct lfqueue_cas_node_s *next, *nextfree; lfq_time_t _deactivate_tm; }; +/*----------------------------------------------------------------------------------------------*/ + //static lfqueue_cas_node_t* __lfq_assigned(lfqueue_t *); -static void __lfq_recycle_free(lfqueue_t *, lfqueue_cas_node_t*); +static void __lfq_recycle_free(lfqueue_t *, lfqueue_cas_node_t *); static void __lfq_check_free(lfqueue_t *); static void *_dequeue(lfqueue_t *); static void *_single_dequeue(lfqueue_t *); -static int _enqueue(lfqueue_t *, void* ); -static inline void* _lfqueue_malloc(void* pl, size_t sz) { - return malloc(sz); +static int _enqueue(lfqueue_t *, void *); +/*----------------------------------------------------------------------------------------------*/ + +static const size_t LFQ_MAX_SINGLE_ALLOCATION = 0xFFFF; + +static inline void *_lfqueue_malloc(void *pl, size_t sz) +{ + /*return malloc(sz);*/ +#ifdef NDEBUG + return calloc(1, sz); +#else + if (sz >= LFQ_MAX_SINGLE_ALLOCATION) + { + errno = ENOMEM; + perror(__FILE__ " -- LFQ max single heap allocation can not be larger than 64KB "); + exit(EXIT_FAILURE); + } + void *p = calloc(1, sz); + if (!p) + { + perror(__FILE__ " -- LFQ heap allocation failed "); + exit(EXIT_FAILURE); + } + return p; +#endif /* NDEBUG */ } -static inline void _lfqueue_free(void* pl, void* ptr) { +static inline void _lfqueue_free(void *pl, void *ptr) +{ free(ptr); } static void * -_dequeue(lfqueue_t *lfqueue) { +_dequeue(lfqueue_t *lfqueue) +{ lfqueue_cas_node_t *head, *next; void *val; - for (;;) { + for (;;) + { head = lfqueue->head; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, head)) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, head)) + { next = head->next; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->tail, head, head)) { - if (next == NULL) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->tail, head, head)) + { + if (next == NULL) + { val = NULL; goto _done; } } - else { - if (next) { + else + { + if (next) + { val = next->value; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, next)) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, next)) + { break; } - } else { + } + else + { val = NULL; goto _done; } @@ -152,27 +203,38 @@ _dequeue(lfqueue_t *lfqueue) { } static void * -_single_dequeue(lfqueue_t *lfqueue) { - lfqueue_cas_node_t *head, *next; - void *val; +_single_dequeue(lfqueue_t *lfqueue) +{ + lfqueue_cas_node_t *head = 0, *next = 0; + void *val = 0; - for (;;) { + for (;;) + { head = lfqueue->head; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, head)) { + assert(head); // DBJ ADDED + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, head)) + { next = head->next; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->tail, head, head)) { - if (next == NULL) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->tail, head, head)) + { + if (next == NULL) + { return NULL; } } - else { - if (next) { + else + { + if (next) + { val = next->value; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, next)) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->head, head, next)) + { lfqueue->_free(lfqueue->pl, head); break; } - } else { + } + else + { return NULL; } } @@ -182,20 +244,24 @@ _single_dequeue(lfqueue_t *lfqueue) { } static int -_enqueue(lfqueue_t *lfqueue, void* value) { +_enqueue(lfqueue_t *lfqueue, void *value) +{ lfqueue_cas_node_t *tail, *node; - node = (lfqueue_cas_node_t*) lfqueue->_malloc(lfqueue->pl, sizeof(lfqueue_cas_node_t)); - if (node == NULL) { + node = (lfqueue_cas_node_t *)lfqueue->_malloc(lfqueue->pl, sizeof(lfqueue_cas_node_t)); + if (node == NULL) + { perror("malloc"); return errno; } node->value = value; node->next = NULL; node->nextfree = NULL; - for (;;) { + for (;;) + { __LFQ_SYNC_MEMORY(); tail = lfqueue->tail; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&tail->next, NULL, node)) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&tail->next, NULL, node)) + { // compulsory swap as tail->next is no NULL anymore, it has fenced on other thread __LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->tail, tail, node); __lfq_check_free(lfqueue); @@ -208,11 +274,13 @@ _enqueue(lfqueue_t *lfqueue, void* value) { } static void -__lfq_recycle_free(lfqueue_t *lfqueue, lfqueue_cas_node_t* freenode) { +__lfq_recycle_free(lfqueue_t *lfqueue, lfqueue_cas_node_t *freenode) +{ lfqueue_cas_node_t *freed; - do { + do + { freed = lfqueue->move_free; - } while (!__LFQ_BOOL_COMPARE_AND_SWAP(&freed->nextfree, NULL, freenode) ); + } while (!__LFQ_BOOL_COMPARE_AND_SWAP(&freed->nextfree, NULL, freenode)); lfq_get_curr_time(&freenode->_deactivate_tm); @@ -220,18 +288,24 @@ __lfq_recycle_free(lfqueue_t *lfqueue, lfqueue_cas_node_t* freenode) { } static void -__lfq_check_free(lfqueue_t *lfqueue) { +__lfq_check_free(lfqueue_t *lfqueue) +{ lfq_time_t curr_time; - if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->in_free_mode, 0, 1)) { + if (__LFQ_BOOL_COMPARE_AND_SWAP(&lfqueue->in_free_mode, 0, 1)) + { lfq_get_curr_time(&curr_time); lfqueue_cas_node_t *rtfree = lfqueue->root_free, *nextfree; - while ( rtfree && (rtfree != lfqueue->move_free) ) { + while (rtfree && (rtfree != lfqueue->move_free)) + { nextfree = rtfree->nextfree; - if ( lfq_diff_time(curr_time, rtfree->_deactivate_tm) > 2) { + if (lfq_diff_time(curr_time, rtfree->_deactivate_tm) > 2) + { // printf("%p\n", rtfree); lfqueue->_free(lfqueue->pl, rtfree); rtfree = nextfree; - } else { + } + else + { break; } } @@ -241,20 +315,21 @@ __lfq_check_free(lfqueue_t *lfqueue) { __LFQ_SYNC_MEMORY(); } -int -lfqueue_init(lfqueue_t *lfqueue) { +int lfqueue_init(lfqueue_t *lfqueue) +{ return lfqueue_init_mf(lfqueue, NULL, _lfqueue_malloc, _lfqueue_free); } -int -lfqueue_init_mf(lfqueue_t *lfqueue, void* pl, lfqueue_malloc_fn lfqueue_malloc, lfqueue_free_fn lfqueue_free) { +int lfqueue_init_mf(lfqueue_t *lfqueue, void *pl, lfqueue_malloc_fn lfqueue_malloc, lfqueue_free_fn lfqueue_free) +{ lfqueue->_malloc = lfqueue_malloc; lfqueue->_free = lfqueue_free; lfqueue->pl = pl; lfqueue_cas_node_t *base = lfqueue_malloc(pl, sizeof(lfqueue_cas_node_t)); lfqueue_cas_node_t *freebase = lfqueue_malloc(pl, sizeof(lfqueue_cas_node_t)); - if (base == NULL || freebase == NULL) { + if (base == NULL || freebase == NULL) + { perror("malloc"); return errno; } @@ -268,7 +343,7 @@ lfqueue_init_mf(lfqueue_t *lfqueue, void* pl, lfqueue_malloc_fn lfqueue_malloc, freebase->nextfree = NULL; freebase->_deactivate_tm = 0; - lfqueue->head = lfqueue->tail = base; // Not yet to be free for first node only + lfqueue->head = lfqueue->tail = base; // Not yet to be free for first node only lfqueue->root_free = lfqueue->move_free = freebase; // Not yet to be free for first node only lfqueue->size = 0; lfqueue->in_free_mode = 0; @@ -276,20 +351,23 @@ lfqueue_init_mf(lfqueue_t *lfqueue, void* pl, lfqueue_malloc_fn lfqueue_malloc, return 0; } -void -lfqueue_destroy(lfqueue_t *lfqueue) { - void* p; - while ((p = lfqueue_deq(lfqueue))) { +void lfqueue_destroy(lfqueue_t *lfqueue) +{ + void *p; + while ((p = lfqueue_deq(lfqueue))) + { lfqueue->_free(lfqueue->pl, p); } // Clear the recycle chain nodes lfqueue_cas_node_t *rtfree = lfqueue->root_free, *nextfree; - while (rtfree && (rtfree != lfqueue->move_free) ) { + while (rtfree && (rtfree != lfqueue->move_free)) + { nextfree = rtfree->nextfree; lfqueue->_free(lfqueue->pl, rtfree); rtfree = nextfree; } - if (rtfree) { + if (rtfree) + { lfqueue->_free(lfqueue->pl, rtfree); } @@ -298,21 +376,23 @@ lfqueue_destroy(lfqueue_t *lfqueue) { lfqueue->size = 0; } -int -lfqueue_enq(lfqueue_t *lfqueue, void *value) { - if (_enqueue(lfqueue, value)) { +int lfqueue_enq(lfqueue_t *lfqueue, void *value) +{ + if (_enqueue(lfqueue, value)) + { return -1; } __LFQ_ADD_AND_FETCH(&lfqueue->size, 1); return 0; } -void* -lfqueue_deq(lfqueue_t *lfqueue) { +void * +lfqueue_deq(lfqueue_t *lfqueue) +{ void *v; - if (//__LFQ_ADD_AND_FETCH(&lfqueue->size, 0) && - (v = _dequeue(lfqueue)) - ) { + if ( //__LFQ_ADD_AND_FETCH(&lfqueue->size, 0) && + (v = _dequeue(lfqueue))) + { __LFQ_FETCH_AND_ADD(&lfqueue->size, -1); return v; @@ -320,10 +400,12 @@ lfqueue_deq(lfqueue_t *lfqueue) { return NULL; } -void* -lfqueue_deq_must(lfqueue_t *lfqueue) { +void * +lfqueue_deq_must(lfqueue_t *lfqueue) +{ void *v; - while ( !(v = _dequeue(lfqueue)) ) { + while (!(v = _dequeue(lfqueue))) + { // Rest the thread for other thread, to avoid keep looping force lfqueue_sleep(1); } @@ -332,12 +414,13 @@ lfqueue_deq_must(lfqueue_t *lfqueue) { } /**This is only applicable when only single thread consume only**/ -void* -lfqueue_single_deq(lfqueue_t *lfqueue) { +void * +lfqueue_single_deq(lfqueue_t *lfqueue) +{ void *v; - if (//__LFQ_ADD_AND_FETCH(&lfqueue->size, 0) && - (v = _single_dequeue(lfqueue)) - ) { + if ( //__LFQ_ADD_AND_FETCH(&lfqueue->size, 0) && + (v = _single_dequeue(lfqueue))) + { __LFQ_FETCH_AND_ADD(&lfqueue->size, -1); return v; @@ -346,10 +429,12 @@ lfqueue_single_deq(lfqueue_t *lfqueue) { } /**This is only applicable when only single thread consume only**/ -void* -lfqueue_single_deq_must(lfqueue_t *lfqueue) { +void * +lfqueue_single_deq_must(lfqueue_t *lfqueue) +{ void *v; - while ( !(v = _single_dequeue(lfqueue)) ) { + while (!(v = _single_dequeue(lfqueue))) + { // Rest the thread for other thread, to avoid keep looping force lfqueue_sleep(1); } @@ -358,12 +443,13 @@ lfqueue_single_deq_must(lfqueue_t *lfqueue) { } size_t -lfqueue_size(lfqueue_t *lfqueue) { +lfqueue_size(lfqueue_t *lfqueue) +{ return __LFQ_ADD_AND_FETCH(&lfqueue->size, 0); } -void -lfqueue_sleep(unsigned int milisec) { +void lfqueue_sleep(unsigned int milisec) +{ #if defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wimplicit-function-declaration" @@ -374,6 +460,4 @@ lfqueue_sleep(unsigned int milisec) { #endif } -#ifdef __cplusplus -} -#endif +/* EOF */ \ No newline at end of file diff --git a/lfqueue.h b/lfqueue.h index 1eafc7b..41d2f44 100644 --- a/lfqueue.h +++ b/lfqueue.h @@ -31,16 +31,62 @@ #ifndef LFQUEUE_H #define LFQUEUE_H +/*----------------------------------------------------------------------------------------------*/ + +#if defined(_WIN32) || defined(__CYGWIN__) +// Windows (x86 or x64) +#define LFQ_WINDOWS +// ... +#elif defined(__linux__) +// Linux +#define LFQ_LINUX +// ... +#elif defined(__APPLE__) && defined(__MACH__) +// Mac OS +#define LFQ_MACOS +// ... +#elif defined(unix) || defined(__unix__) || defined(__unix) +// Unix like OS +#define LFQ_UNIX +// ... +#else +#error Unknown runtime +#endif + +#ifdef NDEBUG +#define LFQ_BUILD "RELEASE" +#define LFQ_RELEASE +#else +#define LFQ_BUILD "DEBUG" +#undef LFQ_DEBUG +#endif + +/*----------------------------------------------------------------------------------------------*/ + #include #include +#define LFQ_NAMESPACE lf_queue + #ifdef __cplusplus -extern "C" { +namespace LFQ_NAMESPACE +{ +extern "C" +{ #endif -typedef struct lfqueue_cas_node_s lfqueue_cas_node_t; -typedef void* (*lfqueue_malloc_fn)(void*, size_t); -typedef void (*lfqueue_free_fn)(void*, void*); + /*----------------------------------------------------------------------------------------------*/ + typedef enum + { + major = 1, + minor = 3, + patch = 0 + } VERSION; + /*----------------------------------------------------------------------------------------------*/ + + typedef struct lfqueue_cas_node_s lfqueue_cas_node_t; + typedef void *(*lfqueue_malloc_fn)(void *, size_t); + typedef void (*lfqueue_free_fn)(void *, void *); #if defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ #define lfq_bool_t int @@ -52,33 +98,35 @@ typedef void (*lfqueue_free_fn)(void*, void*); #endif #endif -typedef struct { - lfqueue_cas_node_t *head, *tail, *root_free, *move_free; - volatile size_t size; - volatile lfq_bool_t in_free_mode; - lfqueue_malloc_fn _malloc; - lfqueue_free_fn _free; - void *pl; -} lfqueue_t; + typedef struct + { + lfqueue_cas_node_t *head, *tail, *root_free, *move_free; + volatile size_t size; + volatile lfq_bool_t in_free_mode; + lfqueue_malloc_fn _malloc; + lfqueue_free_fn _free; + void *pl; + } lfqueue_t; -extern int lfqueue_init(lfqueue_t *lfqueue); -extern int lfqueue_init_mf(lfqueue_t *lfqueue, void* pl, lfqueue_malloc_fn lfqueue_malloc, lfqueue_free_fn lfqueue_free); -extern int lfqueue_enq(lfqueue_t *lfqueue, void *value); -extern void* lfqueue_deq(lfqueue_t *lfqueue); -extern void* lfqueue_single_deq(lfqueue_t *lfqueue); + /*----------------------------------------------------------------------------------------------*/ -/** loop until value been dequeue, it sleeps 1ms if not found to reduce cpu high usage **/ -extern void* lfqueue_deq_must(lfqueue_t *lfqueue); -extern void* lfqueue_single_deq_must(lfqueue_t *lfqueue); + extern int lfqueue_init(lfqueue_t *lfqueue); + extern int lfqueue_init_mf(lfqueue_t *lfqueue, void *pl, lfqueue_malloc_fn lfqueue_malloc, lfqueue_free_fn lfqueue_free); + extern int lfqueue_enq(lfqueue_t *lfqueue, void *value); + extern void *lfqueue_deq(lfqueue_t *lfqueue); + extern void *lfqueue_single_deq(lfqueue_t *lfqueue); -extern void lfqueue_destroy(lfqueue_t *lfqueue); -extern size_t lfqueue_size(lfqueue_t *lfqueue); -extern void lfqueue_sleep(unsigned int milisec); + /** loop until value been dequeue, it sleeps 1ms if not found to reduce cpu high usage **/ + extern void *lfqueue_deq_must(lfqueue_t *lfqueue); + extern void *lfqueue_single_deq_must(lfqueue_t *lfqueue); + extern void lfqueue_destroy(lfqueue_t *lfqueue); + extern size_t lfqueue_size(lfqueue_t *lfqueue); + extern void lfqueue_sleep(unsigned int milisec); #ifdef __cplusplus -} -#endif - +} // extern "C" +} // namespace LFQ_NAMESPACE #endif +#endif /* LFQUEUE_H */ diff --git a/lfqueue_vs201x/lfqueue_vs201x.sln b/lfqueue_vs201x/lfqueue_vs201x.sln deleted file mode 100644 index 4b4f7be..0000000 --- a/lfqueue_vs201x/lfqueue_vs201x.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lfqueue_vs201x", "lfqueue_vs201x.vcxproj", "{791DBD15-5878-4EEB-A9D3-FA94C82E03B7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Debug|x64.ActiveCfg = Debug|x64 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Debug|x64.Build.0 = Debug|x64 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Debug|x86.ActiveCfg = Debug|Win32 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Debug|x86.Build.0 = Debug|Win32 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Release|x64.ActiveCfg = Release|x64 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Release|x64.Build.0 = Release|x64 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Release|x86.ActiveCfg = Release|Win32 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {27ADFC06-52B2-48DA-A1A1-7A8606E0D595} - EndGlobalSection -EndGlobal diff --git a/lfqueue_vs201x/lfqueue_vs201x.vcxproj b/lfqueue_vs201x/lfqueue_vs201x.vcxproj deleted file mode 100644 index 462dc6f..0000000 --- a/lfqueue_vs201x/lfqueue_vs201x.vcxproj +++ /dev/null @@ -1,123 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {791DBD15-5878-4EEB-A9D3-FA94C82E03B7} - lfqueuevs201x - 8.1 - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - - - Level3 - Disabled - true - - - - - Level3 - Disabled - true - - - - - Level3 - MaxSpeed - true - true - true - - - true - true - - - - - Level3 - MaxSpeed - true - true - true - - - true - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/lfqueue_vs201x/lfqueue_vs201x.vcxproj.filters b/lfqueue_vs201x/lfqueue_vs201x.vcxproj.filters deleted file mode 100644 index ccd78a8..0000000 --- a/lfqueue_vs201x/lfqueue_vs201x.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - - - Header Files - - - \ No newline at end of file diff --git a/sample/common.h b/sample/common.h new file mode 100644 index 0000000..0a8033e --- /dev/null +++ b/sample/common.h @@ -0,0 +1,165 @@ +#pragma once +/* +common to all samples +using WIN32 API +*/ + +#include +#include +#include +#include +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define STRICT 1 +#define WIN32_LEAN_AND_MEAN +#include // CRITICAL_SECTION + +#ifdef __cplusplus +namespace common +{ +extern "C" +{ +#endif + + /// -------------------------------------------------------------------------------------------- + /// we need to make common function work in presence of multiple threads + typedef struct synchro_struct + { + bool initalized; + CRITICAL_SECTION crit_sect; + } synchro_type; + + void exit_common(void); + + static inline synchro_type *common_initor() + { + static synchro_type synchro_ = {false}; + if (!synchro_.initalized) + { + InitializeCriticalSection(&synchro_.crit_sect); + synchro_.initalized = true; + atexit(exit_common); + } + + return &synchro_; + } + + static inline void exit_common(void) + { + synchro_type crit_ = *common_initor(); + + if (crit_.initalized) + { + DeleteCriticalSection(&crit_.crit_sect); + crit_.initalized = false; + } + } + + static inline void synchro_enter() { EnterCriticalSection(&common_initor()->crit_sect); } + static inline void synchro_leave() { LeaveCriticalSection(&common_initor()->crit_sect); } + + /// Common_Origin is a "strong type" (c) 2019 dbj@dbj.org + /// + /// void fun(Common_Origin); + /// + /// is infinitely better than + /// + /// void fun ( char * ); + /// + /// calling is even better + /// + /// fun((Common_Origin){"Producer"}); + /// + /// almost like named argument, but not, because + /// the name required is a type + /// thus type and value cleraly stated in a call + /// better than C++ variant: + /// + /// C++ + /// fun({"Producer"}); // what is the type passed? + /// + typedef struct + { + const char *val; + } Common_Origin; + +#define Common_Name_Length 0xFF + typedef struct + { + char val[Common_Name_Length]; + } Common_Name; + + typedef struct + { + unsigned val; + } Common_Id; + + /// -------------------------------------------------------------------------------------------- + /// one can pass objects on stack to threads, but stack is shared by default between threads + /// so for clear confusion free thread to thread messaging use heap + + /// we always `name` the threads by sending them this through + /// _beginthreadex() call, as the fourth argument + inline Common_Name make_name_(Common_Id id_, Common_Origin origin_) + { + synchro_enter(); + assert(origin_.val); + Common_Name retval = {{0}}; + int count = snprintf(retval.val, Common_Name_Length, "%s: %3d", origin_.val, id_.val); + assert(!(count < 0)); + synchro_leave(); + return retval; + } + inline void free_name_(Common_Name name_) + { + synchro_enter(); + memset(name_.val, 0, Common_Name_Length); + synchro_leave(); + } + /// we can not tell if vp_ is coming from a heap + /// or from a stack +// inline char *print_name_(void *vp_, Common_Origin origin_) +// { +// synchro_enter(); +// assert(vp_); +// assert(origin_.val); +// char *pn_ = (char *)vp_; +// #ifdef COMMON_TRACING +// printf("\n%16s [%16s] has started", origin_.val, pn_); +// printf(" in thread [%6d]", (int)GetCurrentThreadId()); +// fflush(0); +// #endif +// synchro_leave(); +// return pn_; +// } + /// ----------------------------------------------------------------------------- +#define Common_Message_Length 0xFF + typedef struct + { + char val[Common_Message_Length]; + } Common_Message; + + /// make message on the heap + /// for sane inter threading + inline Common_Message *make_message_(Common_Origin origin_) + { + synchro_enter(); + assert(origin_.val); + Common_Message *retval = + (Common_Message *)calloc(1, sizeof(Common_Message)); + assert(retval); + int count = snprintf(retval->val, Common_Message_Length, "Message from: %s", origin_.val); + assert(!(count < 0)); + synchro_leave(); + return retval; + } + inline void free_message_(Common_Message *msg_) + { + synchro_enter(); + free(msg_); + synchro_leave(); + } + +#ifdef __cplusplus +} // extern "C" +} // common +#endif diff --git a/sample/cpp/build.cmd b/sample/cpp/build.cmd new file mode 100644 index 0000000..b34f00b --- /dev/null +++ b/sample/cpp/build.cmd @@ -0,0 +1,42 @@ + +@echo off +@cls +@rem of course Windows VCode has to start from msvc developer cmd +@rem debug build on/off -- include/exclude argument -- /Zi +@rem for /d2FH4 see https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ +@rem + +if "%~1"=="R" goto RELEASE +if "%~1"=="r" goto RELEASE + +if "%~1"=="d" goto DEBUG +if "%~1"=="D" goto DEBUG + +goto HELP + +:DEBUG +@echo. +@echo Building for DEBUG -- no exceptions + runtime DLL +@echo. +cl /std:c++17 /MDd -D_HAS_EXCEPTIONS=0 /Zi /Fe: cppsample_dbg.exe cppsample.cpp ../../lfqueue.c +goto EXIT +:RELEASE +@echo. +@echo Building for RELEASE -- no exceptions and DLL runtime +@echo. +cl /std:c++17 /MD -D_HAS_EXCEPTIONS=0 /Fe: cppsample.exe cppsample.cpp ../../lfqueue.c +goto EXIT +:HELP +@cls +@echo. +@echo Arguments are required for %~nx0 +@echo. +@echo D or d for Debug build +@echo R or r for Release build +@echo. +@echo Default is DEBUG build +@echo. +@goto DEBUG +goto EXIT +:EXIT +@dir /D diff --git a/sample/cpp/clean.cmd b/sample/cpp/clean.cmd new file mode 100644 index 0000000..4577393 --- /dev/null +++ b/sample/cpp/clean.cmd @@ -0,0 +1,17 @@ +@echo off +rem cleaning +@cls + +if "%1"=="f" goto FULL +if "%1"=="F" goto FULL +if "%1"=="FULL" goto FULL + +:PARTIAL +@del *.ilk +@del *.pdb +@del *.obj +goto EXIT +:FULL +@del *.exe +goto PARTIAL +:EXIT \ No newline at end of file diff --git a/sample/cpp/cppsample.cpp b/sample/cpp/cppsample.cpp new file mode 100644 index 0000000..39ce5dd --- /dev/null +++ b/sample/cpp/cppsample.cpp @@ -0,0 +1,176 @@ + +/// MSVC, C++17 +/// one consumer, 1..N futures +/// it is important to realize things are NOT happening in-order +/// in multi-threading apps. +/// For example single consumer bellow might start before +/// any of the futures +/// the key in makin things the way you want, is synchronization +#include +#include +#include +#include "../../lfqueue.h" // namespace lf_queue +#include "../common.h" // namespace common + +using namespace std; +using namespace common; +using namespace ::lf_queue; +using namespace std::chrono_literals; +#define MAX_PRODUCER_THREADS 255U + +/// if required Prototype CloseHandle() +/// instead of including the whole windows.h for this one function +/// __declspec(dllimport) +/// extern "C" __declspec(dllimport) int __stdcall CloseHandle(void *hObject); +/// #pragma comment(lib, "Kernel32.lib") +///-------------------------------------------------- +static lf_queue::lfqueue_t the_queue; + +using futures_sequence = vector>; +///-------------------------------------------------- +void __stdcall producer(unsigned producer_threads_count) +{ + using namespace common; + using namespace ::lf_queue; + /* 1: returns first arg cast to char*, does *not* allocate anything */ + Common_Name name_ = make_name_({producer_threads_count}, {"Producer"}); + /* 2: make the message to be sent */ + Common_Message * message_ = make_message_({name_.val}); + /* 3: Enqueue, consumer is responsible freeing the message */ + while (lfqueue_enq(&the_queue, message_)) + { + this_thread::yield(); + } + // producer sends one message and exits + // but before doing so it gives other threads a bit of time + this_thread::yield(); +} + +///-------------------------------------------------- +void __stdcall consumer() +{ + using namespace common; + using namespace ::lf_queue; + const char *consumer_id_ = "99"; + /* using C API provokes cludges */ + /* 1: returns arg cast to char*, does not allocate anything */ + Common_Name name_ = make_name_({99}, {"Consumer"}); + unsigned number_of_messages_received = 0; + // we will loop forever if this is required + while (true) + { + this_thread::yield(); + + /*Dequeue -- This call is only applicable when a single thread consumes messages */ + Common_Message *message_ = + (Common_Message *)lfqueue_single_deq_must(&the_queue); + + if (message_) + { + /* made on the heap by producer, must free it */ + free_message_(message_); + + // Here we decide what is the exiting criteria for the consumer + // for example a special (aka control) message? + // Our logic here is very simple, we exit when number + // of messages equals the number of futures threads + // each of them sends a single message and exits + if (++number_of_messages_received == MAX_PRODUCER_THREADS) + { + printf("\nConsumer has recieved %d messages from %d senders, it is exiting\n", + number_of_messages_received, MAX_PRODUCER_THREADS); + break; // out + } + } + else + { + printf("\nConsumer should not be here.."); + } + } + fflush(0); +} +///-------------------------------------------------- +static int worker(int, char **) +{ + + if (lfqueue_init(&the_queue) == -1) + { + perror("LF QUEUE initialization has failed."); + return EXIT_FAILURE; + } + + unsigned producer_threads_count = MAX_PRODUCER_THREADS; + + futures_sequence futures{0}; + + do + { + futures.push_back(std::async(producer, producer_threads_count)); + } while (--producer_threads_count); + + /// a single consumer is added last + /// NOTE: that does not guarantee it will start last + futures.push_back(std::async(consumer)); + + printf("\n"); + fflush(0); + /// wait for all the futures + unsigned count_ = 1; + for (auto &fut_ : futures) + { + again: + if (fut_.valid()) + fut_.wait(); + else + { + /// you never know ... + printf("\rWaiting for the future %3d, hit CTRL+C if app is stuck here", count_++); + /// give them some time + this_thread::yield(); + goto again; + } + } + + fflush(0); + while (0 != lfqueue_size(&the_queue)) + { + // perror("All queues should be consumed but they are not"); + lfqueue_sleep(1); + // return EXIT_FAILURE; + } + lfqueue_destroy(&the_queue); + fflush(0); + return EXIT_SUCCESS; +} // worker + +///-------------------------------------------------- +/// we can build with or without exceptions enabled +/// +int main(int argc, char **argv) +{ +#if _HAS_EXCEPTIONS + try + { +#endif // _HAS_EXCEPTIONS + return worker(argc, argv); +#if _HAS_EXCEPTIONS + } + catch (std::future_error const &err) + { + printf("\nstd future_error: %s", err.what()); + } + catch (std::system_error &syserr_) + { + printf("\nstd system_error: %s", syserr_.what()); + } + catch (std::exception &ex_) + { + printf("\nstd exception: %s", ex_.what()); + } + catch (...) + { + printf("\nUnknown exception"); + } +#endif // _HAS_EXCEPTIONS + return EXIT_SUCCESS; +} diff --git a/example.c b/sample/linux/example.c similarity index 100% rename from example.c rename to sample/linux/example.c diff --git a/sample/win/build.cmd b/sample/win/build.cmd new file mode 100644 index 0000000..7d3b11d --- /dev/null +++ b/sample/win/build.cmd @@ -0,0 +1,42 @@ + +@echo off +@cls +@rem of course Windows VCode has to start from msvc developer cmd +@rem debug build on/off -- include/exclude argument -- /Zi +@rem for /d2FH4 see https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ +@rem + +if "%~1"=="R" goto RELEASE +if "%~1"=="r" goto RELEASE + +if "%~1"=="d" goto DEBUG +if "%~1"=="D" goto DEBUG + +goto HELP + +:DEBUG +@echo. +@echo Building for DEBUG -- no exceptions + runtime DLL +@echo. +cl /MDd -D_HAS_EXCEPTIONS=0 /Zi /Fe: windows_sample_dbg.exe windows_sample.c ../../lfqueue.c +goto EXIT +:RELEASE +@echo. +@echo Building for RELEASE -- no exceptions and DLL runtime +@echo. +cl /MD -D_HAS_EXCEPTIONS=0 /Fe: windows_sample.exe windows_sample.c ../../lfqueue.c +goto EXIT +:HELP +@cls +@echo. +@echo Arguments are required for %~nx0 +@echo. +@echo D or d for Debug build +@echo R or r for Release build +@echo. +@echo Default is DEBUG build +@echo. +@goto DEBUG +goto EXIT +:EXIT +@dir /D diff --git a/sample/win/clean.cmd b/sample/win/clean.cmd new file mode 100644 index 0000000..4c365ec --- /dev/null +++ b/sample/win/clean.cmd @@ -0,0 +1,4 @@ +rem cleaning +@del *.ilk +@del *.pdb +@del *.obj \ No newline at end of file diff --git a/sample/win/windows_sample.c b/sample/win/windows_sample.c new file mode 100644 index 0000000..13fdf02 --- /dev/null +++ b/sample/win/windows_sample.c @@ -0,0 +1,149 @@ + + +#include +#include +#include +#include +#include +#include +#include "../../lfqueue.h" +#include "../common.h" + +#ifndef LFQ_WINDOWS +#error This is Windows only code +#endif + +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN +#include +#include /* _beginthread, _endthread */ + +/// -------------------------------------------------------------------------------------------- +static lfqueue_t the_queue; +#define MAX_THREADS 42 +static const int total_put = 50000; + +/// -------------------------------------------------------------------------------------------- +unsigned __stdcall producer(void *); +unsigned __stdcall producer(void *arg) +{ +char * name_arg = (char *)arg; + /* make the message to be sent */ + Common_Message *message_ = make_message_((Common_Origin){name_arg}); + /* + Enqueue, consumer is responsible freeing the message + */ + while (lfqueue_enq(&the_queue, message_)) + { + // perror("Q FULL for ?\n"); + lfqueue_sleep(1); + } + // NO! --> free_name_(name_); + lfqueue_sleep(1); + return 0; +} +/// -------------------------------------------------------------------------------------------- +/// running "for ever" in his thread +/// name is coming as argument +unsigned __stdcall consumer(void *); +unsigned __stdcall consumer(void *arg) +{ +printf("\nStarted: %s", (char*)arg ); + +while (1) +{ + /*Dequeue + This call is only applicable when a single thread consumes messages + */ + Common_Message *message_ = lfqueue_single_deq_must(&the_queue); + + if (message_) + { + printf("\nConsumer has received: %-55s", message_->val); + free_message_(message_); + } + else + { + printf("\nConsumer should not be here.."); + } + fflush(0); + lfqueue_sleep(1); +} +return 0; +} + +/*----------------------------------------------------------------------------------------------*/ +int main(int argc, char **argv) +{ + static const int LOOP_SIZE_ = 1; + + if (lfqueue_init(&the_queue) == -1) + { + perror("LF QUEUE initialization has failed."); + return EXIT_FAILURE; + } + + for (int n = 0; n < LOOP_SIZE_; n++) + { + + /* the threads */ + HANDLE threads[MAX_THREADS] = {0}; + + unsigned consumer_thread_idx_ = MAX_THREADS + 100; + + HANDLE consumer_thread = (HANDLE)_beginthreadex( + NULL, 0, consumer, + /* thread data is the name we generate for it */ + make_name_( + (Common_Id){consumer_thread_idx_}, + (Common_Origin){"Consumer"}) + .val, + CREATE_SUSPENDED, NULL); + + /* begin producers, each on a separate thread */ + int i = 0; + do + { + threads[i] = (HANDLE)_beginthreadex( + NULL, 0, producer, + /* thread data is the name we generate for it */ + make_name_( + (Common_Id){i}, (Common_Origin){"Producer"}) + .val, + 0, NULL); + i++; + } while (i < MAX_THREADS); + + /// resume the single consumer + ResumeThread(consumer_thread); + /// wait 5 seconds for consumer + WaitForSingleObject(consumer_thread, 1000 /* 1 second */); + /// shutdown consumer thread + CloseHandle(consumer_thread); + + /// wait for producers + WaitForMultipleObjects(MAX_THREADS, threads, TRUE, INFINITE); + + // Close producers threads + for (int i = 0; i < MAX_THREADS; i++) + CloseHandle(threads[i]); + + } /* main loop */ + + fflush(0); + + while (0 != lfqueue_size(&the_queue)) + { + // perror("All queues should be consumed but they are not"); + lfqueue_sleep(1); + // return EXIT_FAILURE; + } + + lfqueue_destroy(&the_queue); + + printf("\nDone, hit any key you choose to hit today ... \n"); + fflush(0); + getch(); + + return EXIT_SUCCESS; +}