128 Commits
Author SHA1 Message Date
Patrick Pelissier 2d5de1b9ea Update comments 2019-04-29 12:32:29 +02:00
Patrick Pelissier e23691c743 Fix spelling 2019-04-24 07:47:15 +02:00
Patrick Pelissier aaac236db6 Update README 2019-04-22 18:31:47 +02:00
Patrick Pelissier ccccedc2d5 Rework failure messages. 2019-04-22 18:30:46 +02:00
Patrick Pelissier 446afb04c7 Remove unused macro M_INIT_FAILURE 2019-04-22 12:07:53 +02:00
Patrick Pelissier 87cd964329 Uniformize error messages 2019-04-22 11:45:29 +02:00
Patrick Pelissier 80a344e46d Update version 2019-04-22 11:34:35 +02:00
Patrick Pelissier f2eb5014ac Fix regression on 32 bits for strings. 2019-04-21 23:50:42 +02:00
Patrick Pelissier d6c72061ce add new bench on strings 2019-04-21 23:13:29 +02:00
Patrick Pelissier 7c26fd25c9 Add SDS & RAPIDSTRING replace function implemented at used level 2019-04-21 19:54:20 +02:00
Patrick Pelissier 10e12d2b5c Update 2019-04-21 11:21:45 +02:00
Patrick Pelissier b9070f89d9 Update 2019-04-21 10:43:17 +02:00
Patrick Pelissier 46d41a8882 Fix bad merge 2019-04-20 12:33:09 +02:00
Patrick Pelissier b5dc3481d4 Update doc 2019-04-20 12:33:08 +02:00
Patrick Pelissier c18cd81dde Add more assumes needed for static analysis. 2019-04-20 12:33:08 +02:00
Patrick Pelissier eaf5a26157 Minor speed up 2019-04-20 12:33:08 +02:00
Patrick Pelissier 547c67b676 Modify string_t so that it can still be trivially movable once again. 2019-04-20 12:33:08 +02:00
Patrick Pelissier c89d6ef0ce Fix test. 2019-04-20 12:33:08 +02:00
Patrick Pelissier a54041c340 Minor fix 2019-04-20 12:33:08 +02:00
Patrick Pelissier 352e0972cd Comment code 2019-04-20 12:33:08 +02:00
Patrick Pelissier 91ebd7cc9f Cleanup code 2019-04-20 12:33:08 +02:00
Patrick Pelissier d8854c276b Cleanup code 2019-04-20 12:33:08 +02:00
Patrick Pelissier cc61840ae8 Refactor code 2019-04-20 12:33:08 +02:00
Patrick Pelissier 67a7a4762a Refactor code 2019-04-20 12:33:08 +02:00
Patrick Pelissier c28597014f Add more hypothesis 2019-04-20 12:33:08 +02:00
Patrick Pelissier d00f29bf26 Fix bug 2019-04-20 12:33:08 +02:00
Patrick Pelissier 1432fbabca Update buffer to have proper alignement so that we can perform hash computation properly. 2019-04-20 12:33:08 +02:00
Patrick Pelissier f3b6ee0190 Continue porting to new format. 2019-04-20 12:33:08 +02:00
Patrick Pelissier 229d21ef72 Add assume 2019-04-20 12:33:08 +02:00
Patrick Pelissier 00f1b1fc4f Update comment 2019-04-20 12:33:08 +02:00
Patrick Pelissier 277d8b37af Continue porting to new format. 2019-04-20 12:33:08 +02:00
Patrick Pelissier 7b83761ef4 Update 2019-04-20 12:33:08 +02:00
Patrick Pelissier a96e9c7e11 Update string contract 2019-04-20 12:33:08 +02:00
Patrick Pelissier 866721d067 Add first prototype of string with stack allocation. 2019-04-20 12:33:08 +02:00
Patrick Pelissier b356c29c89 Add PGO 2019-04-20 12:29:59 +02:00
Patrick Pelissier a2f7cb7670 Add C Macro Collections ref 2019-04-20 10:24:20 +02:00
Patrick Pelissier 08887c1df0 Add PGO 2019-04-20 09:08:10 +02:00
Patrick Pelissier 8b0a9f2da9 Cleanup Makefile 2019-04-20 08:51:52 +02:00
Patrick Pelissier e6c958c0df Fix data race. 2019-04-19 07:57:10 +02:00
Patrick Pelissier 01ad221972 Rewrite RP so that writers cannot starve and avoid warnings with thread sanitizer. 2019-04-11 20:07:27 +02:00
Patrick Pelissier c269a36e8b Add thread test for RP_CONCURRENT 2019-04-07 19:03:03 +02:00
Patrick Pelissier 95fcd054a3 Fix error in lock orders. 2019-04-04 21:22:37 +02:00
Patrick Pelissier 7760f16260 Fix memory leaks 2019-04-04 08:31:26 +02:00
Patrick Pelissier 6d016d732b Update tags 2019-03-31 22:37:00 +02:00
Patrick Pelissier b9aebf875e Update comments. 2019-03-31 22:36:52 +02:00
Patrick Pelissier 1eb0e8730d Use algorithm with dictionary too. 2019-03-24 08:38:52 +01:00
Patrick Pelissier 7eb26a29f1 Reduce the number of mandatory operators for algorithm:
* EQUAL
* INIT
are not optional (methods depending on them won't be generated
if the operators are not defined).
2019-03-24 08:37:57 +01:00
Patrick Pelissier 0ef051e6d2 Add GLIB to string bench 2019-03-24 08:29:32 +01:00
Patrick Pelissier 0d641f0d17 Update documentation 2019-03-22 07:53:13 +01:00
Patrick Pelissier ce6a7f6cb3 Add missing _it_end for normal dict & export it for both dict 2019-03-15 07:44:58 +01:00
Patrick Pelissier 07b4c1a9f4 Update image 2019-03-12 21:43:33 +01:00
Patrick Pelissier 4d3a82583d Update doc 2019-03-12 21:43:18 +01:00
Patrick Pelissier 2835d53ef1 Clean up code. 2019-03-12 21:42:53 +01:00
Patrick Pelissier 620ecadec1 Update doc 2019-03-12 07:49:04 +01:00
Patrick Pelissier dc803f23bb Add missing const 2019-03-09 16:38:44 +01:00
Patrick Pelissier a6d11fe897 Update doc (ALGO part) 2019-03-09 16:38:25 +01:00
Patrick Pelissier 78b05690e7 Update README 2019-03-07 19:28:46 +01:00
Patrick Pelissier 958a79ad73 Add another example 2019-03-07 19:25:39 +01:00
Patrick Pelissier ead25c905a Factorize common macro for FLA 2019-03-07 07:42:50 +01:00
Patrick Pelissier 97f80450ca Update issue 2019-02-21 20:19:04 +01:00
Patrick Pelissier 8e62537604 Update ISSUE 2019-02-20 11:46:58 +01:00
Patrick Pelissier 7813182e2b Update test. 2019-02-19 20:22:55 +01:00
Patrick Pelissier b199fef674 Update comments and move commented asm into common function. 2019-02-18 11:02:13 +01:00
Patrick Pelissier e27c06c227 Fix regression build with SDS & rapidstring 2019-02-18 10:55:51 +01:00
Patrick Pelissier 41b04f34e2 Fix: Uses of __thread after static keyword 2019-02-18 10:53:27 +01:00
Patrick Pelissier b091f84f90 Update code after benching empty creation of string. 2019-02-18 10:48:03 +01:00
Patrick Pelissier 05ae9f382b Update the barrier used to prevent the compiler from over-optimizing.
Shall be could enought except if using FLTO.
2019-02-18 10:47:28 +01:00
Patrick Pelissier a36efcd888 Create new helper compiler_barrier to prevent compiler from over-optimizing bench. 2019-02-18 10:46:21 +01:00
Patrick Pelissier af2fe9ccf9 Fix issue (sizeof instead of offsetof) 2019-02-17 09:54:56 +01:00
Patrick Pelissier c4a5727c2a Fix deref of NULL pointer. 2019-02-17 09:50:04 +01:00
Patrick Pelissier 31ca762c62 Add a new test case for empty string. 2019-02-17 09:49:49 +01:00
Patrick Pelissier 30bda70319 Fix issue. 2019-02-17 09:36:18 +01:00
Patrick Pelissier 7fa9d375b8 Add bench of small non empty constructor 2019-02-17 08:35:11 +01:00
Patrick Pelissier 91ae3fd4f2 Add memory barrier to limiting the effect of the optimizer on inline implementation 2019-02-16 23:20:36 +01:00
Patrick Pelissier b00d6a9655 Add CSV output to string bench 2019-02-16 22:30:39 +01:00
Patrick Pelissier 013aa58524 Update documentation 2019-02-16 09:01:57 +01:00
Patrick Pelissier 8ce7e0483f Update comment 2019-02-13 08:24:21 +01:00
Patrick Pelissier 582c413cd2 Add VLA allocation compatible with concurrent mempool & gc 2019-02-13 08:19:07 +01:00
Patrick Pelissier 22e16e983b Separate mempool in two concepts:
* A Garbage Collector that centralizes the connexion with the threads
  and registers all mempools
* the mempool that is responsible for alloc/dec of nodes.

It allows having multiple mempools registered in a GC.
2019-02-10 10:26:20 +01:00
Patrick Pelissier 7c0c16260a Update doc 2019-02-05 22:56:02 +01:00
Patrick Pelissier e496fbd152 Add test for string_t + OA DICT 2019-01-27 22:59:03 +01:00
Patrick Pelissier e025de91ab Adding OOR methods for string_t 2019-01-27 22:32:47 +01:00
Patrick Pelissier 69e2fc432f Update doc about limitation of operator TEST_EQUAL 2019-01-27 21:16:19 +01:00
Patrick Pelissier 78ba83ce42 * Fix BOUNDED_STRING_CTE macro. In OOR context, it may create invalid constant that triggers
assumption fault.
* Fix _equal_p in OOR context for bounded string. This method can be called in OOR context,
so the contracts may not be verified.
2019-01-27 17:33:47 +01:00
Patrick Pelissier 6d9b61c119 Reindent 2019-01-27 17:33:17 +01:00
Patrick Pelissier 61cfa5cedd Use of proper contract for BOUNDED_STRING. 2019-01-26 14:00:32 +01:00
Patrick Pelissier 0507d94306 Add test for IO for BOUNDED STRING 2019-01-26 13:39:47 +01:00
Patrick Pelissier 7e8919080b Add IO for BOUNDED STRING 2019-01-26 13:38:54 +01:00
Patrick Pelissier 8406efcaab Add test case of BOUNDED_STRING + OA DICT 2019-01-26 12:03:13 +01:00
Patrick Pelissier 2d87eb1e6a Adding default OOR_EQUAL & OOR_SET for BOUNDED_STRING
to be used in OA dict
2019-01-26 12:02:50 +01:00
Patrick Pelissier 4685aac454 Add test for hash of empty string 2019-01-26 09:10:36 +01:00
Patrick Pelissier 9863a8325c Fix assertion. NULL pointer is accepted for HASH providing the size is 0 2019-01-26 09:10:18 +01:00
Patrick Pelissier b9514cbe72 Fix usage of API with OOR EQUAL of OA Dict 2019-01-25 20:37:33 +01:00
Patrick Pelissier 7b085d18fc Add test case of OA DICT + concurrent 2019-01-25 20:19:52 +01:00
Patrick Pelissier 91e73520f3 Fix issue of non being able to use API_2 with OOR_SET for OA dict 2019-01-25 20:19:31 +01:00
Patrick Pelissier aebf5c83ba Update description of STORED_HASH dict as per remark of #44 2019-01-25 19:47:47 +01:00
Patrick Pelissier 42ff47f511 Fix issue described in #44 (missing types for OA dict) 2019-01-25 19:47:46 +01:00
Patrick Pelissier 746bdf144b Fix warning with CLANG7 2019-01-24 07:44:36 +01:00
Patrick Pelissier 4e8f5f7b51 Add test for queue SPSC 2019-01-21 22:20:28 +01:00
Patrick Pelissier 50d89f71cb Update 2019-01-21 22:06:31 +01:00
Patrick Pelissier d70ef53283 Add _push_force for QUEUE SPSC 2019-01-20 23:59:33 +01:00
Patrick Pelissier 1b6182c1cd Update 2019-01-20 23:57:33 +01:00
Patrick Pelissier 73b50c852a Add bench for SPSC in BULK mode 2019-01-16 05:08:03 +01:00
Patrick Pelissier 063011d8e9 Adding yield in case of failure of push/pop 2019-01-16 04:46:06 +01:00
Patrick Pelissier 0f3bf6ab82 Add _bulk version for SPSC queue. To test/bench 2019-01-15 23:27:02 +01:00
Patrick Pelissier 475836e851 Use of new macro M_CACHELINE_ALIGN 2019-01-15 19:11:10 +01:00
Patrick Pelissier 325ca30c61 Add M_CACHELINE_ALIGN macro 2019-01-15 19:10:39 +01:00
Patrick Pelissier 72e872c768 Remove outdated comment 2019-01-15 07:34:19 +01:00
Patrick Pelissier c2a837b30b Update 2019-01-15 07:32:13 +01:00
Patrick Pelissier 08822ac7d5 Update 2019-01-15 07:25:11 +01:00
Patrick Pelissier 7f9007ca1a Add UPDATE_TYPE 2019-01-11 07:58:20 +01:00
Patrick Pelissier f067cb334c Add missing return after M_MEMORY_FULL 2019-01-05 19:57:53 +01:00
Patrick Pelissier 60ee253fa7 Update 2019-01-05 13:15:50 +01:00
Patrick Pelissier 4642e0b506 Workaround issue of stdatomic on MSYS2/GCC 2019-01-05 00:19:09 +01:00
Patrick Pelissier bd29680b62 Add explanations as comment 2019-01-03 07:45:08 +01:00
Patrick Pelissier c337462af6 Use extension to silent warnings about C11 features used in C99 2019-01-02 22:37:40 +01:00
Patrick Pelissier c990277855 Add extension attribute support 2019-01-02 22:34:39 +01:00
Patrick Pelissier 8870a1c2f9 Remove atomic qualifier for one variable (useless) 2019-01-02 22:16:08 +01:00
Patrick Pelissier 60f1454550 Fix incorrect qualifier position for emulation layer (issue for atomic pointer definition) 2019-01-02 22:15:47 +01:00
Patrick Pelissier 38eb1c26c6 Variable number of threads for concurrent memory pool 2019-01-02 21:48:53 +01:00
Patrick Pelissier af050d0100 Port to C++ madness 2019-01-02 21:21:24 +01:00
Patrick Pelissier 14f3ae5cea Fix issue 2019-01-02 21:21:06 +01:00
Patrick Pelissier f4ed750d39 Add first test case for concurrent memory pool 2019-01-02 21:13:01 +01:00
Patrick Pelissier 4b3adc8d35 Add first WIP Concurrent Memory Allocator 2019-01-02 20:45:06 +01:00
Patrick Pelissier 8b3a4a802c Happy new year! 2019-01-02 18:12:07 +01:00
Patrick Pelissier 8b9f605ffa Add exponential backoff structure & functions 2019-01-02 18:09:16 +01:00
Patrick Pelissier 3c56f24db2 Update analysis 2018-12-29 00:16:50 +01:00
Patrick Pelissier 82dbf60a4c Use of the standard naming for User Defined macro that can customize the code generation. Rename macros WORKER_DISABLE. 2018-12-23 23:49:19 +01:00
69 changed files with 3877 additions and 1179 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2017-2018, Patrick Pelissier
Copyright (c) 2017-2019, Patrick Pelissier
All rights reserved.
Redistribution and use in source and binary forms, with or without
+1 -1
View File
@@ -10,7 +10,7 @@ PREFIX=/usr/local
# Package name & version.
PACKAGE=m*lib-$(VERSION)
VERSION=0.2.1
VERSION=0.2.2
# Define the contain of the distribution tarball
# TODO: Get theses lists from GIT itself.
+416 -174
View File
File diff suppressed because it is too large. Load diff
+55 -34
View File
@@ -1,14 +1,20 @@
CC=cc -std=c11
CXX=c++ -std=c++11
CFLAGS=-O2 -march=native -Wall
XCFLAGS=
CPPFLAGS=-I..
N=4
BENCH_DEF=
RM=rm -rf
.PHONY: all container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
.PHONY: all pgo container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
all: container queue mempool string plain
pgo:
$(MAKE) all XCFLAGS="-fprofile-generate=pgo"
$(MAKE) all XCFLAGS="-fprofile-use=pgo"
#########################################################################
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap
@@ -32,7 +38,7 @@ plain: bench-plain
bench-mlib:
$(CC) $(CFLAGS) $(CPPFLAGS) -pthread bench-mlib.c -o bench-mlib.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -pthread bench-mlib.c -o bench-mlib.exe
@./bench-mlib.exe 10
@./bench-mlib.exe 11
@./bench-mlib.exe 20
@@ -67,9 +73,10 @@ bench-mlib-thread:
@./bench-mlib-thread.exe 63
@./bench-mlib-thread.exe 64
@./bench-mlib-thread.exe 65
@./bench-mlib-thread.exe 66
bench-stl:
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-stl.cpp -o bench-stl.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-stl.cpp -o bench-stl.exe
@./bench-stl.exe 10
@./bench-stl.exe 20
@./bench-stl.exe 30
@@ -84,7 +91,7 @@ bench-stl:
bench-qt:
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
bench-qt0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
@./bench-qt.exe 10
@./bench-qt.exe 20
@./bench-qt.exe 30
@@ -97,7 +104,7 @@ bench-qt0:
bench-glib:
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
bench-glib0:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
@./bench-glib.exe 10
@./bench-glib.exe 20
@./bench-glib.exe 30
@@ -110,7 +117,7 @@ bench-klib:
@if test -n "$${KLIB}" ; then $(MAKE) bench-klib0 ; else echo "Nothing to be done for KLIB." ; fi
bench-klib0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c -o bench-klib.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c -o bench-klib.exe
@./bench-klib.exe 10
@./bench-klib.exe 20
@./bench-klib.exe 30
@@ -123,7 +130,7 @@ bench-libdynamic:
@if test -n "$${LIBDYNAMIC}" ; then $(MAKE) bench-libdynamic0 ; else echo "Nothing to be done for LIBDYNAMIC." ; fi
bench-libdynamic0:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-libdynamic.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-libdynamic.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
@./bench-libdynamic.exe 10
@./bench-libdynamic.exe 20
@./bench-libdynamic.exe 30
@@ -136,7 +143,7 @@ bench-sparsepp:
@if test -n "$${SPARSEPP}" ; then $(MAKE) bench-sparsepp0 ; else echo "Nothing to be done for SPARSEPP." ; fi
bench-sparsepp0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp -o bench-sparsepp.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp -o bench-sparsepp.exe
@./bench-sparsepp.exe 41
@./bench-sparsepp.exe 42
@./bench-sparsepp.exe 43
@@ -146,7 +153,7 @@ bench-densehashmap:
@if test -n "$${DENSEHASHMAP}" ; then $(MAKE) bench-densehashmap0 ; else echo "Nothing to be done Google DENSEHASHMAP." ; fi
bench-densehashmap0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp -o bench-densehashmap.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp -o bench-densehashmap.exe
@./bench-densehashmap.exe 41
@./bench-densehashmap.exe 42
@./bench-densehashmap.exe 43
@@ -156,7 +163,7 @@ bench-collectionc:
@if test -n "$${COLLECTIONC}" ; then $(MAKE) bench-collectionc0 ; else echo "Nothing to be done for COLLECTIONC." ; fi
bench-collectionc0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c -lcollectc -o bench-collectionc.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c -lcollectc -o bench-collectionc.exe
@./bench-collectionc.exe 10
@./bench-collectionc.exe 20
@./bench-collectionc.exe 30
@@ -169,7 +176,7 @@ bench-tommyds:
@if test -n "$${TOMMYDS}" ; then $(MAKE) bench-tommyds0 ; else echo "Nothing to be done for TOMMYDS." ; fi
bench-tommyds0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c -o bench-tommyds.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c -o bench-tommyds.exe
@./bench-tommyds.exe 10
@./bench-tommyds.exe 20
@./bench-tommyds.exe 30
@@ -181,7 +188,7 @@ bench-flathashmap:
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
bench-flathashmap0:
$(CXX) -std=c++14 $(CFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
@./bench-flathashmap.exe 41
@./bench-flathashmap.exe 42
@./bench-flathashmap.exe 43
@@ -191,7 +198,7 @@ bench-hopscotchmap:
@if test -n "$${HOPSCOTCHMAP}" ; then $(MAKE) bench-hopscotchmap0 ; else echo "Nothing to be done for HOPSCOTCHMAP." ; fi
bench-hopscotchmap0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}" bench-hopscotchmap.cpp -o bench-hopscotchmap.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}" bench-hopscotchmap.cpp -o bench-hopscotchmap.exe
@./bench-hopscotchmap.exe 41
@./bench-hopscotchmap.exe 42
@./bench-hopscotchmap.exe 43
@@ -201,7 +208,7 @@ bench-uthash:
@if test -n "$${UTHASH}" ; then $(MAKE) bench-uthash0 ; else echo "Nothing to be done for UTHASH." ; fi
bench-uthash0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c -o bench-uthash.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c -o bench-uthash.exe
@./bench-uthash.exe 10
@./bench-uthash.exe 20
@./bench-uthash.exe 41
@@ -214,7 +221,7 @@ bench-qlibc:
@if test -n "$${QLIBC}" ; then $(MAKE) bench-qlibc0 ; else echo "Nothing to be done for QLIBC." ; fi
bench-qlibc0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c -o bench-qlibc.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c -o bench-qlibc.exe
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 10
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 20
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 30
@@ -222,11 +229,12 @@ bench-qlibc0:
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 41
# LIBSRT shall point to the directory where the source are
# The library shall be built.
bench-libsrt:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-libsrt0 ; else echo "Nothing to be done for LIBSRT." ; fi
bench-libsrt0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
@./bench-libsrt.exe 10
@./bench-libsrt.exe 20
@./bench-libsrt.exe 41
@@ -240,7 +248,7 @@ bench-libsrt0:
bench-liblfds:
@if test -n "$${LIBLFDS}" ; then $(MAKE) bench-liblfds0 ; else echo "Nothing to be done for LIBLFDS." ; fi
bench-liblfds0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
@LD_LIBRARY_PATH="$${LIBLFDS}/bin:$${LD_LIBRARY_PATH}" ./bench-liblfds.exe 60
@@ -249,36 +257,37 @@ bench-liblfds0:
bench-concurrentqueue:
@if test -n "$${CONCURRENTQUEUE}" ; then $(MAKE) bench-concurrentqueue0 ; else echo "Nothing to be done for CONCURRENTQUEUE." ; fi
bench-concurrentqueue0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp -o bench-concurrentqueue.exe -lpthread
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp -o bench-concurrentqueue.exe -lpthread
@./bench-concurrentqueue.exe 60
# XXHASH shall point to the source directory of xxhash
# The library shall be built.
bench-xxhash:
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
bench-xxhash0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
@./bench-xxhash.exe 70
# BOOST shall point to the header location.
bench-boost:
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
bench-boost0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp -o bench-boost.exe -lpthread
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp -o bench-boost.exe -lpthread
@./bench-boost.exe 60
# NEDTRIES shall point to the header location.
bench-nedtries:
@if test -n "$${NEDTRIES}" ; then $(MAKE) bench-nedtries0 ; else echo "Nothing to be done for NEDTRIES." ; fi
bench-nedtries0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c -o bench-nedtries.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c -o bench-nedtries.exe
@./bench-nedtries.exe 40
@./bench-nedtries.exe 45
############################################################################
bench-plain:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
@./bench-plain-c.exe
############################################################################
@@ -286,13 +295,13 @@ bench-plain:
bench-mempool:
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
bench-mempool0:
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
@./bench-mempool.exe
############################################################################
bench-maxdict:
$(CC) $(CFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
@./bench-max-dict.exe
############################################################################
@@ -300,27 +309,39 @@ bench-maxdict:
# BSTRLIB shall point to the BSTRLIB sources
# LIBSRT shall point to the LIBSRT sources
bench-string:
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-with BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-with ; fi
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-step1 BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-step1 ; fi
bench-string-with:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step1:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF)"; fi
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
bench-string-with2:
@if test -n "$${SDS}" ; then $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step2:
@if test -n "$${SDS}" ; then $(MAKE) bench-string-step3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-step3 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-with3:
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step3:
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-exe:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BSTRLIB}" -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp -o bench-string.exe $(BENCH_DEF)
bench-string-step4:
@if test -n "$${GLIB}" ; then $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_GLIB $$(pkg-config --cflags --libs glib-2.0)" ; else $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step5:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp common.c $(BENCH_DEF) -o bench-string.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string-2.cpp common.c $(BENCH_DEF) -o bench-string-2.exe
@./bench-string.exe
@./bench-string-2.exe 2000000 0
@./bench-string-2.exe 2000000 1
@./bench-string-2.exe 2000000 2
@./bench-string-2.exe 2000000 3
bench-string-pgo:
$(MAKE) bench-string XCFLAGS="-fprofile-generate=pgo"
$(MAKE) bench-string XCFLAGS="-fprofile-use=pgo"
############################################################################
clean:
rm -f *.o bench-*.exe bench-*.s *~ massif.out.*
$(RM) *.o bench-*.exe bench-*.s *~ massif.out.*
$(RM) pgo
cd nucleotide && $(MAKE) clean
############################################################################
+96 -4
View File
@@ -441,7 +441,7 @@ static void conso_mpmc(void *arg)
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
while (!queue_uint_pop(&j, g_buff_mpmc));
while (!queue_uint_pop(&j, g_buff_mpmc)) m_thread_yield();
s += j;
}
while (!queue_ull_push(g_final_mpmc, s));
@@ -453,7 +453,7 @@ static void prod_mpmc(void *arg)
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!queue_uint_push(g_buff_mpmc, r ));
while (!queue_uint_push(g_buff_mpmc, r )) m_thread_yield();
r = r * 31421U + 6927U;
}
}
@@ -526,7 +526,7 @@ static void conso_spsc(void *arg)
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
while (!queue_single_uint_pop(&j, g_buff_spsc));
while (!queue_single_uint_pop(&j, g_buff_spsc)) m_thread_yield();
s += j;
}
while (!queue_single_ull_push(g_final_spsc, s));
@@ -538,7 +538,7 @@ static void prod_spsc(void *arg)
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!queue_single_uint_push(g_buff_spsc, r ));
while (!queue_single_uint_push(g_buff_spsc, r )) m_thread_yield();
r = r * 31421U + 6927U;
}
}
@@ -581,6 +581,96 @@ static void test_queue_single(size_t n)
/********************************************************************************************/
#define BULK_SIZE 20
static void final_spsc_bulk(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long j, s = 0;
for(int i = 0; i < n;i++) {
while (!queue_single_ull_pop(&j, g_final_spsc)) m_thread_yield();
s += j;
}
g_result = s;
}
static void conso_spsc_bulk(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long s = 0;
unsigned tab[BULK_SIZE];
for(int i = 0; i < n;i+= BULK_SIZE) {
unsigned k = queue_single_uint_pop_bulk(BULK_SIZE, tab, g_buff_spsc);
while (k != BULK_SIZE) {
m_thread_yield();
k += queue_single_uint_pop_bulk(BULK_SIZE-k, tab+k, g_buff_spsc);
}
for(k = 0; k < BULK_SIZE;k++)
s += tab[k];
}
while (!queue_single_ull_push(g_final_spsc, s));
}
static void prod_spsc_bulk(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
if ((n % BULK_SIZE) != 0) abort();
size_t r = n;
unsigned tab[BULK_SIZE];
for(unsigned int i = 0; i < n;i+= BULK_SIZE) {
for(unsigned k = 0; k < BULK_SIZE; k++) {
tab[k] = r;
r = r * 31421U + 6927U;
}
unsigned k = queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE, tab);
while (k != BULK_SIZE) {
m_thread_yield();
k += queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE-k, tab+k);
}
}
}
static void test_queue_single_bulk(size_t n)
{
const int cpu_count = 2;
const int prod_count = cpu_count/2;
const int conso_count = cpu_count - prod_count;
// Init
queue_single_uint_init(g_buff_spsc, 64*cpu_count);
queue_single_ull_init (g_final_spsc, 64*cpu_count);
// Create thread
m_thread_t idx_p[prod_count];
m_thread_t idx_c[conso_count];
m_thread_t idx_final;
for(int i = 0; i < prod_count; i++) {
m_thread_create (idx_p[i], prod_spsc_bulk, &n);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso_spsc_bulk, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final_spsc_bulk, &n2);
// Wait for jobs to be done.
for(int i = 0; i < prod_count; i++) {
m_thread_join(idx_p[i]);
}
for(int i = 0; i < conso_count; i++) {
m_thread_join(idx_c[i]);
}
m_thread_join(idx_final);
// Clear & quit
queue_single_ull_clear(g_final_spsc);
queue_single_uint_clear(g_buff_spsc);
}
/********************************************************************************************/
DEQUE_DEF(deque_uint, unsigned int)
CONCURRENT_DEF(cdeque_uint, deque_uint_t, M_OPEXTEND(DEQUE_OPLIST(deque_uint, M_DEFAULT_OPLIST), PUSH(deque_uint_push_front)))
@@ -739,6 +829,8 @@ int main(int argc, const char *argv[])
test_function("Queue SPSC time", 1000000, test_queue_single);
if (n == 65)
test_function("Queue CONCURRENT time", 1000000, test_queue_concurrent);
if (n == 66)
test_function("Queue SPSC BULK time", 1000000, test_queue_single_bulk);
if (n == 70) {
n = (argc > 2) ? atoi(argv[2]) : 100000000;
test_hash_prepare(n);
+288
View File
@@ -0,0 +1,288 @@
/*
* M*LIB - String Bench
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "m-string.h"
#include "common.h"
#ifdef BENCH_CAN_USE_SDS
extern "C" {
#include "sds.h"
};
#endif
#ifdef BENCH_CAN_USE_RAPIDSTRING
#include "rapidstring.h"
#endif
/*
* BENCH
* 2000000
* 1. Generate N strings of random numbers 32 bits.
* 2. Concat all strings in a string usigned a random permutation of N.
* 3. Replace "1234" in the string into "WELL" until nothing remains.
* 4. Replace "56789" in the string into "DONE" until nothing remains.
* Return the final string length.
*/
unsigned *permutation_tab = NULL;
void random_permutation(unsigned n)
{
if (permutation_tab) free(permutation_tab);
permutation_tab = (unsigned *) malloc(n * sizeof(unsigned));
if (permutation_tab == NULL) abort();
for(unsigned i = 0; i < n; i++)
permutation_tab[i] = i;
for(unsigned i = 0; i < n; i++) {
unsigned j = rand_get() % n;
unsigned k = rand_get() % n;
unsigned l = permutation_tab[j];
permutation_tab[j] = permutation_tab[k];
permutation_tab[k] = l;
}
}
using namespace std;
size_t bench_stl(unsigned n)
{
vector<string> tab;
tab.resize(n);
for(unsigned i = 0; i < n; i++)
tab[i] = to_string(rand_get());
string str;
for(unsigned i = 0; i < n; i++) {
str += tab[permutation_tab[i]];
}
size_t pos = 0;
do {
pos = str.find("1234", pos);
if (pos != string::npos) {
str.replace (pos, 4, "WELL");
}
} while (pos != string::npos);
pos = 0;
do {
pos = str.find("56789", pos);
if (pos != string::npos) {
str.replace (pos, 5, "DONE");
}
} while (pos != string::npos);
return str.length();
}
size_t bench_mlib(unsigned n)
{
string_t *tab = (string_t*) malloc (n * sizeof (string_t));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
string_init(tab[i]);
string_printf(tab[i], "%u", rand_get());
}
// P2
string_t str;
string_init(str);
for(unsigned i = 0; i < n; i++) {
string_cat(str, tab[permutation_tab[i]]);
}
// P3
size_t i = 0;
do {
i = string_replace_str(str, "1234", "WELL", i);
} while (i != STRING_FAILURE);
// P4
i = 0;
do {
i = string_replace_str(str, "56789", "DONE", i);
} while (i != STRING_FAILURE);
size_t length = string_size(str);
// Clean
string_clear(str);
for(unsigned i = 0; i < n; i++) {
string_clear(tab[i]);
}
free(tab);
return length;
}
#ifdef BENCH_CAN_USE_SDS
sds SDS_replace_at(sds str, size_t pos, size_t len, const char str2[])
{
// simple implementation as replace is not available
sds a = sdsnewlen((const void*) str, pos);
a = sdscat(a, str2);
a = sdscat(a, &str[pos+len]);
sdsfree(str);
return a;
}
size_t bench_sds(unsigned n)
{
sds *tab = (sds*) malloc (n * sizeof (sds));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
tab[i] = sdsfromlonglong(rand_get());
}
// P2
sds str = sdsempty();
for(unsigned i = 0; i < n; i++) {
str = sdscat(str, tab[permutation_tab[i]]);
}
// P3
size_t pos = 0;
do {
char *p = strstr(&str[pos], "1234");
pos = p == NULL ? -1U : (p - str);
if (pos != -1U) {
str = SDS_replace_at(str, pos, 4, "WELL");
}
} while (pos != -1U);
// P4
pos = 0;
do {
char *p = strstr(&str[pos], "56789");
pos = p == NULL ? -1U : (p - str);
if (pos != -1U) {
str = SDS_replace_at(str, pos, 5, "DONE");
}
} while (pos != -1U);
size_t length = sdslen(str);
// Clean
sdsfree(str);
for(unsigned i = 0; i < n; i++) {
sdsfree(tab[i]);
}
free(tab);
return length;
}
#endif
#ifdef BENCH_CAN_USE_RAPIDSTRING
void RAPIDSTRING_replace_at(rapidstring *str, size_t pos, size_t len, const char str2[])
{
// simple implementation as replace is not available
rapidstring a;
rs_init_w_n(&a, rs_data(str), pos);
rs_cat(&a, str2);
rs_cat(&a, &rs_data(str)[pos+len] );
rs_free(str);
*str = a;
}
size_t bench_rapidstring(unsigned n)
{
rapidstring *tab = (rapidstring*) malloc (n * sizeof (rapidstring));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
char tmp[10];
sprintf(tmp, "%u", rand_get());
rs_init_w(&tab[i], tmp);
}
// P2
rapidstring str;
rs_init(&str);
for(unsigned i = 0; i < n; i++) {
rs_cat_rs(&str, &tab[permutation_tab[i]]);
}
// P3
size_t pos = 0;
do {
char *p = strstr(&rs_data(&str)[pos], "1234");
pos = p == NULL ? -1U : (p - rs_data(&str));
if (pos != -1U) {
RAPIDSTRING_replace_at(&str, pos, 4, "WELL");
}
} while (pos != -1U);
// P4
pos = 0;
do {
char *p = strstr(&rs_data(&str)[pos], "56789");
pos = p == NULL ? -1U : (p - rs_data(&str));
if (pos != -1U) {
RAPIDSTRING_replace_at(&str, pos, 5, "DONE");
}
} while (pos != -1U);
size_t length = rs_len(&str);
// Clean
rs_free(&str);
for(unsigned i = 0; i < n; i++) {
rs_free(&tab[i]);
}
free(tab);
return length;
}
#endif
int main(int argc, const char *argv[])
{
unsigned length, n = atoi(argv[1]);
unsigned select = atoi(argv[2]);
const char *name;
random_permutation(n);
rand_init();
unsigned long long t0 = cputime();
switch (select) {
case 0:
length = bench_mlib(n);
name = "MLIB";
break;
case 1:
length = bench_stl(n);
name = "STL";
break;
#ifdef BENCH_CAN_USE_SDS
case 2:
length = bench_sds(n);
name = "SDS";
break;
#endif
#ifdef BENCH_CAN_USE_RAPIDSTRING
case 3:
length = bench_rapidstring(n);
name = "RAPID";
break;
#endif
default:
exit(0);
}
unsigned long long t1 = cputime();
printf("%5.5s LENGTH=%u T= %f s\n", name, length, (t1-t0) / 1000000.0 );
return 0;
}
+522 -169
View File
File diff suppressed because it is too large. Load diff
+34
View File
@@ -0,0 +1,34 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common.h"
void compiler_barrier(void *p)
{
// Do nothing, but let the compiler thinks that we did.
(void) p;
// Can be used for FLTO building
// asm volatile("": "+r" (p) : :"memory");
}
+17 -7
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -25,11 +25,14 @@
#ifndef __COMMON_BENCH__H
#define __COMMON_BENCH__H
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
/* WINDOWS variant */
#include <windows.h>
static unsigned long long
static inline unsigned long long
cputime (void)
{
LARGE_INTEGER freq, val;
@@ -44,7 +47,7 @@ cputime (void)
#include <sys/resource.h>
#include <sys/time.h>
static unsigned long long
static inline unsigned long long
cputime (void)
{
#if defined(MULTI_THREAD_MEASURE)
@@ -62,23 +65,25 @@ cputime (void)
#endif
unsigned long g_result;
static unsigned long g_result;
static
#if defined(MULTI_THREAD_MEASURE)
M_THREAD_ATTR
#endif
unsigned int randValue = 0;
static void rand_init(void)
static inline void rand_init(void)
{
randValue = 0;
}
static unsigned int rand_get(void)
static inline unsigned int rand_get(void)
{
randValue = randValue * 31421U + 6927U;
return randValue;
}
static void test_function(const char str[], size_t n, void (*func)(size_t))
static inline void test_function(const char str[], size_t n, void (*func)(size_t))
{
unsigned long long start, end;
rand_init();
@@ -123,4 +128,9 @@ static inline int get_cpu_count(void)
#endif
}
#ifdef __cplusplus
extern "C"
#endif
void compiler_barrier(void *p);
#endif
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
BIN
View File
Binary file not shown.
+64 -132
View File
@@ -1,6 +1,58 @@
*Long term issues*
------------------
* TODO #24 : Add MIN-MAX-HEAP container :ENHANCEMENT:
See https://en.wikipedia.org/wiki/Min-max_heap
as DPRIORITY_QUEUE_DEF ?
* TODO #23: Force MOVE semantic :ENHANCEMENT:
Some type may need to have a force MOVE semantic (for example, they can store
pointer to themselves). Currently the INIT_MOVE & MOVE operators are more
a help for performance than a strict semantic usage.
** ARRAY container
The ARRAY container doesn't support strict MOVE semantic for example.
It is not a simple matter as it performs a realloc of the table, thus
moving the data before they can be moved using MOVE. Two solutions:
- New operator MOVE_SELF to fix a type after it has been moved.
- If MOVE defined, force another table and then copy by hand the type. This will be slower and consumme more memory.
** MOVE in OPLIST :ENHANCEMENT:
Proposal: do not export INIT_MOVE / MOVE operator in OPLIST if the MOVE operator is
compatible with a pure COPY semantic. An exported MOVE operator will tell
other containers than the type shall be carefully moved using the provided
MOVE operator.
For example for tuple, it shall
- create an init_move operator if no one has disabled INIT_MOVE,
- export the init_move operator if at least one has exported a INIT_MOVE and no one has disabled INIT_MOVE.
* DONE #22: Enhanced services for SPSC Queue :ENHANCEMENT:
Add services:
** DONE _push_bulk
Test the capacity of the queue and push as much as possible in the queue
with one check of the atomic structure.
** DONE _pop_bulk
Test the capacity of the queue and push as much as possible in the queue
with one check of the atomic structure.
** DONE _push_force:
If the capacity of the queue is full, pop one element and push it:
push always succeed and the queue always keep the youngest element.
* TODO #21: Generic Binary serialization :ENHANCEMENT:
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
@@ -91,144 +143,24 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
Implement a lock free list. Most of the difficulty is the memory reclamation part.
Typically this lock free list shall be compatible with RCU method.
A node can be in the following state:
** First step: backoff methods :DONE:
** Second step: lock free node pool : :DONE:
Done as m-c-mempool header.
- FREE: Can be used for allocation.
- IN_USE: The node is currently used in the list.
- TO_BE_RECLAIMED: The node is semantically removed, but some others may still be used it.
** Third step: Implement generic lock free list on top of it.
So separate lists shall be used for the memory handler.
It shall also be compatible with NUMA and avoid putting all nodes in the same lists if possible:
otherwise the head will become a hot spot and slow down everything.
The ABA problem shall also be taken into account. Normaly with the provided state of the nodes,
it should not be a problem.
The ABA problem is already taken into account by the memory alloctor
provided that the lock free list doesn't try to be smart.
backoff should also be considered when using CAS.
backoff has be used when using CAS.
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be taken into account.
** First step: lock free node pool :
Questions:
- singly or doubly or dual push?
- needs to be logically deleted : needs a previous field
(NULL if not logically deleted) ? TBC
*** API based on Lock-Free Queue List
-- node_t is { node_t *next; uint ticket; data_t }
-- freelist_t : basic internal lockfree list used to construct more user friendly list (FIFO queue to avoid putting constraints on free & alloc at the same times + it is needed for reclaim part).
do not ensure consistensy of data.
is { _Atomic(node*) head, tail; }
Atomic Queue List : See algorithm by Michael & Scott on Lock Free Queue List (aka MSList)
+ freelist_push (&list, &node)
+ freelist_pop(&list) return node
+ freelist_pop_if(&list, ticket) return node Needs minor adaptation on _pop.
-- lf_memory_core_t : basic type responsible for the centralization of the list allocation.
+ init(lf_mc_t &, int max_thread, size_t alloc_size)
+ clear(lf_mc_t &)
+ lc_mc_t is {
atomic_uint ticket;
atomic_uint reg_thread; // ==> genint instead
atomic_uint tab_ticket[MAX_THREAD];
freelist_t free[MAX_THREAD];
freelist_t to_be_reclaimed[MAX_THREAD];
}
-- lf_memory_thread_t: specialization for the current thread
* lf_mt_t is { int thd_idx; lf_mc_t *global;}
* init(lf_mt_t &, &lf_mc_t g) is { thd_idx := atomic_add(&g.reg_tread, 1); .global := g; }
* clear is ??
* awake () is preparing the thread to handle operations on the list { .global.tab_ticket[.thd_idx] := atomic_add(&global.ticket, 1)+1; }
* sleep () is sleeping the thread so that it doesn't handle operations on the list and perform memory reclamation if needed {
.global.tab_ticket[.thd_idx] := UINT_MAX;
min_ticket := min(global.tab_ticket);
scan the list to_be_reclaimed while node.cpt < min_ticket,
pop the node from to_be_reclaimed and push it to the list free
lf_memory_thread_t shall be in awake state to do anything on the list.
*** Bench
Basic malloc / free: 9.6s
LF Node : 10.6s
Even if LF Node ensures a correct life cycle of the node for the read, the performance is passable.
Too much CAS:
51.40 6.13 6.13 51146587 0.00 0.00 lf_freelist_push
20.10 8.53 2.40 28143911 0.00 0.00 lf_freelist_pop
15.65 10.40 1.87 253579 0.01 0.02 lf_mempool_sleep
*** Mixed API
To overcome this limitation performs a mixed approache:
- having a simple (non atomic) list of nodes to alloc quickly the nodes.
- embedded within an atomic list of list of nodes for GC.
Thread data will be:
- FreeList is a non atomic singly linked list.
- ToBeReclaimedList is the same
- FreeListList is an atomic queue list of non atomic singly list.
- CanBeReclaimedListList is an atomic queue list of non atomic singly list
**** alloc() returns node:
- Pop from FreeList a node.
- If failure, atomic pop from FreeListList a list of list of free nodes. If success, update FreeList with the new list and pop a node from it.
- If failure, try an atomic from the FreeListList of other threads. If success, update FreeList with the new list and pop a node from it.
- If failure, malloc a new pool of FreeListList (TBD: all nodes in one pass? Needs a separate list to handle final free of nodes).
*** free(node)
- Push node in ToBeReclaimedList
*** GarbageCollector called on sleep:
- Compute min_ticket for all threads
- Atomic push of ToBeReclaimedList in CanBeReclaimedListList
- for each thread starting from mine:
-- Atomic pop from CanBeReclaimedListList[] if max ticket of pool of nodes < min_ticket and atomic push in FreeListList
max ticket of pool is the ticket of the top node of the list.
==> Should be much faster since there is much less CAS operations performed and GC is perfomed per block.
** Second step: lf_list_t is a stack based list based on RCU:
_read: classic non atomic operations.
_push(list, data, lf_mt_t) is {
allocate new node from lf_mt_t.global.free, or malloc if nothing.
copy data into node
head := atomic_load(&list.head)
do
node->next := head
while !CAS(&list.head, &head, node)
# ABA problem shall not happen since node cannot be reused immediately
_pop(list, data, lf_mt_t) is {
head := atomic_load(&list.head)
do {
if head == NULL return NULL
} while !CAS(&list.head, &head, head.next)
copy head.data into data
head.cpt := atomic_read(&global.ticket)
push node in global.to_be_reclaimed
}
_erase() TODO
_update() TODO
** Example / Way to use:
- init global memory handler.
- for each thread,
-- init thread memory handler XX,
-- ...
-- XX.awake()
-- ...
-- do as many stuff on any list as we want (this is hot spot).
-- ...
-- XX.sleep() # Memory can be reclaimed
-- XX.awake()
* TODO #15: Constructor / Destructor Prologue / Epilogue for Stack Exception Handling :ENHANCEMENT:
@@ -280,7 +212,7 @@ Needs some formal operator in the oplist to support it fully and an example.
- EXTRA_DATA: Add an extra-data field wihtin the container. Defines the type of data.
It is a kind of object inheritance where the container inherits some extra data from its base.
* TODO #12: Atomic shared pointer :ENHANCEMENT:
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 91 KiB

+40
View File
@@ -0,0 +1,40 @@
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#include <stdio.h>
#include "m-array.h"
#include "m-tuple.h"
#include "m-dict.h"
#include "m-string.h"
TUPLE_DEF2(symbol, (offset, long), (value, long))
#define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
ARRAY_DEF(array_symbol, symbol_t)
#define M_OPL_array_symbol_t() ARRAY_OPLIST(array_symbol, M_OPL_symbol_t())
DICT_DEF2(sections, string_t, array_symbol_t)
#define M_OPL_sections_t() DICT_OPLIST(sections, STRING_OPLIST, M_OPL_array_symbol_t())
int main(int argc, const char *argv[])
{
if (argc < 2) abort();
FILE *f = fopen(argv[1], "rt");
if (!f) abort();
M_LET(sc, sections_t) {
sections_in_str(sc, f);
array_symbol_t *a = sections_get(sc, STRING_CTE(".text"));
if (a == NULL) {
printf("There is no .text section.");
} else {
printf("Section .text is :");
array_symbol_out_str(stdout, *a);
printf("\n");
}
}
return 0;
}
#else
int main(void) { return 0; }
#endif
+84 -75
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - ALGO module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -105,6 +105,8 @@
#define ALGOI_DEF_P2(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
M_IF_METHOD(EQUAL, type_oplist)( \
\
/* It supposes that the container is not sorted */ \
static inline void \
M_C(name, _find_again) (it_t it, type_t const data) \
@@ -117,20 +119,90 @@
} \
\
static inline void \
M_C(name, _find) (it_t it, container_t l, type_t const data) \
M_C(name, _find) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_FIRST(cont_oplist, it, l); \
M_C(name, _find_again)(it, data); \
} \
\
static inline bool \
M_C(name, _contain_p) (container_t l, type_t const data) \
M_C(name, _contain_p) (container_t const l, type_t const data) \
{ \
it_t it; \
M_C(name,_find)(it, l, data); \
return !M_CALL_IT_END_P(cont_oplist, it); \
} \
\
/* For the definition of _find_last, if the methods \
PREVIOUS & IT_LAST are defined, then search backwards */ \
M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \
( \
static inline void \
M_C(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
for (M_CALL_IT_LAST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_PREVIOUS(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
/* We can stop as soon as a match is found */ \
return; \
} \
} \
, \
/* Otherwise search forward, but don't stop on the first occurence */ \
static inline void \
M_C(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_END(cont_oplist, it, l); \
it_t it2; \
for (M_CALL_IT_FIRST(cont_oplist, it2, l); \
!M_CALL_IT_END_P(cont_oplist, it2) ; \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \
/* We cannot stop as soon as a match is found */ \
M_CALL_IT_SET(cont_oplist, it, it2) ; \
} \
} \
) /* End of alternative of _find_last */ \
\
static inline size_t \
M_C(name, _count) (container_t const l, type_t const data) \
{ \
it_t it; \
size_t count = 0; \
for (M_CALL_IT_FIRST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_NEXT(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
count++ ; \
} \
return count; \
} \
\
\
static inline void \
M_C(name, _mismatch_again) (it_t it1, it_t it2) \
{ \
for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
!M_CALL_IT_END_P(cont_oplist, it2); \
M_CALL_IT_NEXT(cont_oplist, it1), \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \
*M_CALL_IT_CREF(cont_oplist, it2))) \
break; \
} \
} \
\
static inline void \
M_C(name, _mismatch) (it_t it1, it_t it2, container_t const l1, container_t const l2 ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
M_CALL_IT_FIRST(cont_oplist, it2, l2); \
M_C(name, _mismatch_again)(it1, it2); \
} \
\
, /* NO EQUAL */) \
\
static inline void \
M_C(name, _find_again_if) (it_t it, bool (*func)(type_t const)) \
{ \
@@ -148,54 +220,9 @@
M_C(name, _find_again_if)(it, func); \
} \
\
/* For the definition of _find_last, if the methods \
PREVIOUS & IT_LAST are defined, then search backwards */ \
M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \
( \
static inline void \
M_C(name, _find_last) (it_t it, container_t l, type_t const data) \
{ \
for (M_CALL_IT_LAST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_PREVIOUS(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
/* We can stop as soon as a match is found */ \
return; \
} \
} \
, \
/* Otherwise search forward, but don't stop on the first occurence */ \
static inline void \
M_C(name, _find_last) (it_t it, container_t l, type_t const data) \
{ \
M_CALL_IT_END(cont_oplist, it, l); \
it_t it2; \
for (M_CALL_IT_FIRST(cont_oplist, it2, l); \
!M_CALL_IT_END_P(cont_oplist, it2) ; \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \
/* We cannot stop as soon as a match is found */ \
M_CALL_IT_SET(cont_oplist, it, it2) ; \
} \
} \
) /* End of alternative of _find_last */ \
\
static inline size_t \
M_C(name, _count) (container_t l, type_t const data) \
{ \
it_t it; \
size_t count = 0; \
for (M_CALL_IT_FIRST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_NEXT(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
count++ ; \
} \
return count; \
} \
\
static inline size_t \
M_C(name, _count_if) (container_t l, bool (*func)(type_t const data)) \
M_C(name, _count_if) (container_t const l, bool (*func)(type_t const data)) \
{ \
it_t it; \
size_t count = 0; \
@@ -209,27 +236,6 @@
} \
\
static inline void \
M_C(name, _mismatch_again) (it_t it1, it_t it2) \
{ \
for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
!M_CALL_IT_END_P(cont_oplist, it2); \
M_CALL_IT_NEXT(cont_oplist, it1), \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \
*M_CALL_IT_CREF(cont_oplist, it2))) \
break; \
} \
} \
\
static inline void \
M_C(name, _mismatch) (it_t it1, it_t it2, container_t l1, container_t l2 ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
M_CALL_IT_FIRST(cont_oplist, it2, l2); \
M_C(name, _mismatch_again)(it1, it2); \
} \
\
static inline void \
M_C(name, _mismatch_again_if) (it_t it1, it_t it2, bool (*func)(type_t const, type_t const) ) \
{ \
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
@@ -243,7 +249,7 @@
} \
\
static inline void \
M_C(name, _mismatch_if) (it_t it1, it_t it2, container_t l1, \
M_C(name, _mismatch_if) (it_t it1, it_t it2, container_t const l1, \
container_t l2, bool (*func)(type_t const, type_t const) ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
@@ -260,7 +266,8 @@
} \
\
static inline void \
M_C(name, _reduce) (type_t *dest, container_t l, void (*f)(type_t *, type_t const) ) \
M_C(name, _reduce) (type_t *dest, container_t const l, \
void (*f)(type_t *, type_t const) ) \
{ \
bool initDone = false; \
for M_EACH(item, l, cont_oplist) { \
@@ -273,9 +280,10 @@
} \
} \
\
M_IF_METHOD(INIT, type_oplist)( \
static inline \
void M_C(name, _map_reduce) (type_t *dest, \
container_t l, \
const container_t l, \
void (*redFunc)(type_t*, type_t const), \
void (*mapFunc)(type_t*, type_t const) ) \
{ \
@@ -293,9 +301,10 @@
} \
M_CALL_CLEAR(type_oplist, tmp); \
} \
, ) \
\
static inline bool \
M_C(name, _any_of_p) (container_t l, bool (*f)(type_t const) ) \
M_C(name, _any_of_p) (container_t const l, bool (*f)(type_t const) ) \
{ \
for M_EACH(item, l, cont_oplist) { \
if (f(*item)) \
@@ -305,7 +314,7 @@
} \
\
static inline bool \
M_C(name, _all_of_p) (container_t l, bool (*f)(type_t const) ) \
M_C(name, _all_of_p) (container_t const l, bool (*f)(type_t const) ) \
{ \
for M_EACH(item, l, cont_oplist) { \
if (!f(*item)) \
+7 -7
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic ARRAY module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -32,7 +32,7 @@
#define ARRAY_DEF(name, ...) \
ARRAYI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
/* Define the oplist of a dynamic array given its name and its oplist.
USAGE: ARRAY_OPLIST(name[, oplist of the type]) */
@@ -49,7 +49,7 @@
/* OPLIST definition of a dynamic array */
/* FIXME: Do we want to export some methods as they are slow and
are not fit to be used for building other methods (like _remove)? */
are not fit to be used for building other methods (like _it_remove)? */
#define ARRAYI_OPLIST2(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
@@ -103,7 +103,7 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* Define the contract of an array */
/* Define the internal contract of an array */
#define ARRAYI_CONTRACT(a) do { \
assert (a != NULL); \
assert (a->size <= a->alloc); \
@@ -119,7 +119,7 @@
typedef struct M_C(name, _s) { \
size_t size; /* Number of elements in the array */ \
size_t alloc; /* Allocated size for the array */ \
type *ptr; /* Pointer to the array */ \
type *ptr; /* Pointer to the array base */ \
} array_t[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
@@ -127,8 +127,8 @@
typedef type M_C(name, _type_t); \
\
typedef struct M_C(name, _it_s) { \
size_t index; \
const struct M_C(name, _s) *array; \
size_t index; /* Index of the element */ \
const struct M_C(name, _s) *array; /* Reference of the array */ \
} array_it_t[1]; \
\
static inline void \
+18 -6
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin stdatomic wrapper for C++ compatibility
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -98,7 +98,7 @@ using std::memory_order_seq_cst;
#define _Atomic(T) std::atomic< T >
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|| ( defined(__GNUC__) && !defined(__clang__) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|| (defined(__clang__) && __clang_major__ >= 4)
@@ -106,6 +106,18 @@ using std::memory_order_seq_cst;
and doesn't provide its own stdatomic.h */
#include <stdatomic.h>
/* MSYS2 has a conflict between cdefs.h which defines a _Atomic macro (if not C11)
not compatible with the used stdatomic.h (from GCC).
Provide a configurable mechanism to undef it with auto-detection of msys2 / gcc */
#ifndef M_USE_UNDEF_ATOMIC
# if defined(__MSYS__) && defined(__GNUC__) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L)
# define M_USE_UNDEF_ATOMIC 1
# endif
#endif
#if defined(M_USE_UNDEF_ATOMIC) && M_USE_UNDEF_ATOMIC == 1
# undef _Atomic
#endif
#else
/* Non working atomic.h, nor working stdatomic.h found.
@@ -125,10 +137,10 @@ using std::memory_order_seq_cst;
*/
#define _Atomic(T) \
struct { \
volatile T _val; \
T _zero; \
T _previous; \
m_mutex_t _lock; \
T volatile _val; \
T _zero; \
T _previous; \
m_mutex_t _lock; \
}
/* Even if memory order is defined, only the strongest constraint is used */
+21 -18
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - BITSET module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -30,33 +30,21 @@
/********************************** INTERNAL ************************************/
// Define a limb of a bitset
// Define the limb of a bitset
typedef uint64_t bitset_limb;
// And its size
#define BITSET_LIMB_BIT (sizeof(bitset_limb) * CHAR_BIT)
typedef struct bitset_s {
size_t size; // Size is the number of bits
size_t alloc; // Alloc is the number of allocated limbs
bitset_limb *ptr; // Pointer to the allocated limbs
} bitset_t[1];
typedef struct bitset_s *bitset_ptr;
typedef const struct bitset_s *bitset_srcptr;
typedef struct bitset_it_s {
size_t index; // index to the array of bit
bool value; // value used for _ref & _cref to store the value
struct bitset_s *set; // the associated bitset
} bitset_it_t[1];
// bitset grow policy
// bitset grow policy. n is limb size
#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 4 ? 4 : (n) * 2)
// Compute the number of allocated limbs needed to handle 'n' bits.
#define BITSETI_TO_ALLOC(n) (((n) + BITSET_LIMB_BIT - 1) / BITSET_LIMB_BIT)
// Compute the number of bits available from the allocated size
// Compute the number of bits available from the allocated size in limbs
#define BITSETI_FROM_ALLOC(n) ((n) * BITSET_LIMB_BIT)
// Contract of a bitset
#define BITSETI_CONTRACT(t) do { \
assert (t != NULL); \
assert (t->size <= BITSETI_FROM_ALLOC (t->alloc)); \
@@ -68,6 +56,21 @@ typedef struct bitset_it_s {
/********************************** EXTERNAL ************************************/
typedef struct bitset_s {
size_t size; // Size is the number of bits
size_t alloc; // Alloc is the number of allocated limbs
bitset_limb *ptr; // Pointer to the allocated limbs
} bitset_t[1];
typedef struct bitset_s *bitset_ptr;
typedef const struct bitset_s *bitset_srcptr;
typedef struct bitset_it_s {
size_t index; // index to the array of bit
bool value; // value used for _ref & _cref to store the value
struct bitset_s *set; // the associated bitset
} bitset_it_t[1];
static inline void
bitset_init(bitset_t t)
{
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - B+TREE module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+88 -17
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Fixed size BUFFER & STACK interface
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -29,12 +29,12 @@
#include "m-mutex.h"
#include "m-atomic.h"
/* Define the different kind of policy a buffer can have:
/* Define the different kind of policy a lock-based buffer can have:
* - the buffer can be either a queue (policy is FIFO) or a stack (policy is FILO),
* - if the push is blocking (waiting for the buffer to has some space) or not,
* - if the pop is blocking (waiting for the buffer to has some space) or not,
* - if both push / pop are blocking,
* - if it shall be thread safe or not (i.e. remove mutex and atomic costs),
* - if the push method is by default blocking (waiting for the buffer to has some space) or not,
* - if the pop method is by default blocking (waiting for the buffer to has some data) or not,
* - if both methods are blocking,
* - if it shall be thread safe or not (i.e. remove the mutex lock and atomic costs),
* - if the buffer has to be init with empty elements, or if it shall init an element when it is pushed (and moved when popped),
* - if the buffer has to overwrite the last element if the buffer is full,
* - if the pop of an element is not complete until the call to pop_release (preventing push until this call).
@@ -626,22 +626,22 @@ M_C(name, _init)(buffer_t v, size_t size) \
typedef struct M_C(name, _el_s) { \
atomic_uint seq; /* Can only increase until overflow */ \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_uint)+sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_uint)-sizeof(type) : 1]; \
M_CACHELINE_ALIGN(align, atomic_uint, type); \
} M_C(name, _el_t); \
\
/* If there is only one producer and one consummer, then they won't \
typically use the same cache line, increasing performance. */ \
typedef struct M_C(name, _s) { \
atomic_uint ProdIdx; /* Can only increase until overflow */ \
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_CACHELINE_ALIGN(align1, atomic_uint); \
atomic_uint ConsoIdx; /* Can only increase until overflow */ \
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_CACHELINE_ALIGN(align2, atomic_uint); \
M_C(name, _el_t) *Tab; \
unsigned int size; \
} buffer_t[1]; \
\
static inline bool \
M_C(name, _push)(buffer_t table, type x) \
M_C(name, _push)(buffer_t table, type const x) \
{ \
QUEUEI_MPMC_CONTRACT(table); \
unsigned int idx = atomic_load_explicit(&table->ProdIdx, \
@@ -820,12 +820,12 @@ M_C(name, _init)(buffer_t v, size_t size) \
atomic_uint consoIdx; /* Can only increase until overflow */ \
unsigned int size; \
M_C(name, _el_t) *Tab; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_uint) +sizeof(size_t) + sizeof(void*) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_uint) -sizeof(size_t)-sizeof(void*): 1]; \
M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_C(name, _el_t) *); \
atomic_uint prodIdx; /* Can only increase until overflow */ \
} buffer_t[1]; \
\
static inline bool \
M_C(name, _push)(buffer_t table, type x) \
M_C(name, _push)(buffer_t table, type const x) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -867,6 +867,81 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline unsigned \
M_C(name, _push_bulk)(buffer_t table, unsigned n, type const x[]) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
assert (x != NULL); \
assert (n <= table->size); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_acquire); \
unsigned int max = M_MIN(n, table->size - (w-r) ); \
if (max == 0) \
return 0; \
for(unsigned int k = 0; k < max; k++) { \
unsigned int i = (w+k) & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, table->Tab[i].x, x[k]); \
} else { \
M_CALL_INIT_SET(oplist, table->Tab[i].x, x[k]); \
} \
} \
atomic_store_explicit(&table->prodIdx, w+max, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
return max; \
} \
\
static inline unsigned \
M_C(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
assert (ptr != NULL); \
assert (n <= table->size); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_relaxed); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_acquire); \
if (w-r == 0) \
return 0; \
unsigned int max = M_MIN(w-r, n); \
for(unsigned int k = 0; k < max; k++) { \
unsigned int i = (r+k) & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, ptr[k], table->Tab[i].x); \
} else { \
M_DO_INIT_MOVE (oplist, ptr[k], table->Tab[i].x); \
} \
} \
atomic_store_explicit(&table->consoIdx, r+max, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
return max; \
} \
\
static inline void \
M_C(name, _push_force)(buffer_t table, type const x) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_acquire); \
/* If no place in queue, try to skip the last one */ \
while (w-r >= table->size) { \
bool b = atomic_compare_exchange_strong(&table->consoIdx, &r, r+1); \
r += b; \
} \
unsigned int i = w & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, table->Tab[i].x, x); \
} else { \
M_CALL_INIT_SET(oplist, table->Tab[i].x, x); \
} \
atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
} \
\
static inline size_t \
M_C(name, _size)(buffer_t table) \
{ \
@@ -954,8 +1029,4 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
\
// NOTE: SET & INIT_SET are deliberatly missing. TBC if it the right way.
// NOTE: There is no oplist defined for this container.
#endif
+823
View File
@@ -0,0 +1,823 @@
/*
* M*LIB - Concurrent memory pool allocator
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_CONCURRENT_MEMPOOL_H
#define MSTARLIB_CONCURRENT_MEMPOOL_H
#include "m-core.h"
#include "m-atomic.h"
#include "m-genint.h"
/* Minimum number of nodes per group of nodes */
#define C_MEMPOOL_MIN_NODE_PER_GROUP 16
#define C_MEMPOOL_DEF(name, type_t) \
C_MEMPOOL_DEF_SINGLY_LIST(name, type_t) \
C_MEMPOOL_DEF_LF_QUEUE(name, type_t) \
C_MEMPOOL_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
C_MEMPOOL_DEF_SYSTEM_ALLOC(name, type_t) \
C_MEMPOOL_DEF_LF_MEMPOOL(name, type_t)
/* Classic Singly List without allocation */
#define C_MEMPOOL_DEF_SINGLY_LIST(name, type_t) \
\
typedef struct M_C(name, _slist_node_s) { \
struct M_C(name, _slist_node_s) *next; \
type_t data; \
} M_C(name, _slist_node_t); \
\
typedef struct M_C(name, _slist_node_s) *M_C(name, _slist_t)[1]; \
\
static inline void \
M_C(name, _slist_init)(M_C(name, _slist_t) list) \
{ \
*list = NULL; \
} \
\
static inline void \
M_C(name, _slist_push)(M_C(name, _slist_t) list, \
M_C(name, _slist_node_t) *node) \
{ \
node->next = *list; \
*list = node; \
} \
\
static inline M_C(name, _slist_node_t) * \
M_C(name, _slist_pop)(M_C(name, _slist_t) list) \
{ \
assert (*list != NULL); \
M_C(name, _slist_node_t) *node = *list; \
*list = node->next; \
M_IF_DEBUG(node->next = NULL;) \
return node; \
} \
\
static inline bool \
M_C(name, _slist_empty_p)(M_C(name, _slist_t) list) \
{ \
return *list == NULL; \
} \
\
static inline void \
M_C(name, _slist_move)(M_C(name, _slist_t) list, \
M_C(name, _slist_t) src) \
{ \
*list = *src; \
*src = NULL; \
} \
\
static inline void \
M_C(name, _slist_clear)(M_C(name, _slist_t) list) \
{ \
M_C(name, _slist_node_t) *it = *list, *next; \
while (it) { \
next = it->next; \
M_MEMORY_DEL(it); \
it = next; \
} \
*list = NULL; \
} \
/* Lock Free free queue list (not generic one) of lists without allocation
Based on Michael & Scott Lock Free Queue List algorithm.
Each list is considered empty if there is only one node within.
This LF Queue List doesn't try to prevent the ABA problem. It is up to the
caller to avoid recycling the nodes too fast.
Each list has its own unique NIL ptr in order to avoid issues when
migrating a node from a Q to another: in the following scenario,
- Thread 1 performs a PUSH of N in Q1 with Q1 empty (only node is NA)
NA.next is NIL.
- Thread 1 is interrupted just before the CAS on NA.next
- Thread 2 performs a sucessfull push of NB in Q1. NA.next is set to NB.
- Thread 2 performs a sucessfull pop of NA in Q1
- Thread 2 performs a sucessfull push of NA in Q2. NA.next is set to NIL.
- Thread 1 is restored and will succeed as NA.next is once again NIL.
In order to prevent the last CAS to succeed, each queue uses its own NIL pointer.
It is a derived problem of the ABA problem.
*/
/* TODO: Optimize alignement to reduce memory consumption. NIL object can use []
to reduce memory consumption too (non compatible with C++ ...) */
#define C_MEMPOOL_DEF_LF_QUEUE(name, type_t) \
\
typedef struct M_C(name, _lf_node_s) { \
M_ATTR_EXTENSION _Atomic(struct M_C(name, _lf_node_s) *) next; \
m_gc_atomic_ticket_t cpt; \
M_C(name, _slist_t) list; \
} M_C(name, _lf_node_t); \
\
typedef struct M_C(name, _lflist_s) { \
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) head; \
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) tail; \
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_C(name, _lf_node_t) nil; \
} M_C(name, _lflist_t)[1]; \
\
static inline void \
M_C(name, _lflist_init)(M_C(name, _lflist_t) list, \
M_C(name, _lf_node_t) *node) \
{ \
atomic_init(&list->head, node); \
atomic_init(&list->tail, node); \
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
} \
\
static inline bool \
M_C(name, _lflist_empty_p)(M_C(name, _lflist_t) list) \
{ \
return atomic_load(&list->tail) == atomic_load(&list->head); \
} \
\
static inline void \
M_C(name, _lflist_push)(M_C(name, _lflist_t) list, \
M_C(name, _lf_node_t) *node, m_backoff_t bkoff) \
{ \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
\
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
m_backoff_reset(bkoff); \
while (true) { \
tail = atomic_load(&list->tail); \
next = atomic_load_explicit(&tail->next, memory_order_acquire); \
if (M_UNLIKELY(next != &list->nil)) { \
/* Tail was not pointing to the last node \
Try to swing Tail to the next node */ \
atomic_compare_exchange_weak_explicit(&list->tail, \
&tail, next, \
memory_order_release, \
memory_order_relaxed); \
} else { \
/* Try to link node at the end of the linked list */ \
if (atomic_compare_exchange_strong_explicit(&tail->next, \
&next, node, \
memory_order_release, \
memory_order_relaxed)) \
break; \
m_backoff_wait(bkoff); \
} \
} \
/* Enqueue is done. Try to swing Tail to the inserted node \
If it fails, someone else will do it or has already did it. */ \
atomic_compare_exchange_strong_explicit(&list->tail, &tail, node, \
memory_order_acq_rel, \
memory_order_relaxed); \
} \
\
static inline M_C(name, _lf_node_t) * \
M_C(name, _lflist_pop)(M_C(name, _lflist_t) list, m_backoff_t bkoff) \
{ \
M_C(name, _lf_node_t) *head; \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
\
/* Reinitialize backoff */ \
m_backoff_reset(bkoff); \
while (true) { \
head = atomic_load(&list->head); \
tail = atomic_load(&list->tail); \
next = atomic_load(&head->next); \
/* Are head, tail, and next consistent?*/ \
if (M_LIKELY(head == \
atomic_load_explicit(&list->head, memory_order_relaxed))) \
{ \
/* Is queue empty or Tail falling behind? */ \
if (head == tail) { \
/* Is queue empty? */ \
if (next == &list->nil) \
return NULL; \
/* Tail is falling behind. Try to advance it */ \
atomic_compare_exchange_strong_explicit(&list->tail, &tail, \
next, \
memory_order_release, \
memory_order_relaxed); \
} else { \
/* Try to swing Head to the next node */ \
if (atomic_compare_exchange_strong_explicit(&list->head, \
&head, next, \
memory_order_release, \
memory_order_relaxed)) { \
break; \
} \
/* Failure: perform a random exponential backoff */ \
m_backoff_wait(bkoff); \
} \
} \
} \
/* dequeue returns an element that becomes the new dummy element (the new head), \
and the former dummy element (the former head) is removed: \
Since we want a link of free list, and we don't care about the content itsef, \
provided that the node we return is older than the one we should return, \
Therefore, we return the previous dummy head. \
As such, it is not the original MSqueue algorithm. */ \
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
return head; \
} \
\
/* Dequeue a node if the node is old enough */ \
static inline M_C(name, _lf_node_t) * \
M_C(name, _lflist_pop_if)(M_C(name, _lflist_t) list, \
m_gc_ticket_t age, m_backoff_t bkoff) \
{ \
M_C(name, _lf_node_t) *head; \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
\
m_backoff_reset(bkoff); \
while (true) { \
head = atomic_load(&list->head); \
tail = atomic_load(&list->tail); \
next = atomic_load(&head->next); \
if (M_LIKELY(head == atomic_load_explicit(&list->head, memory_order_relaxed))) \
{ \
if (head == tail) { \
if (next == &list->nil) \
return NULL; \
atomic_compare_exchange_strong_explicit(&list->tail, &tail, next, \
memory_order_release, \
memory_order_relaxed); \
} else { \
/* Test if the node is old enought to be popped */ \
if (atomic_load_explicit(&next->cpt, memory_order_relaxed) >= age) \
return NULL; \
/* Try to swing Head to the next node */ \
if (atomic_compare_exchange_strong_explicit(&list->head, \
&head, next, \
memory_order_release, \
memory_order_relaxed)) { \
break; \
} \
m_backoff_wait(bkoff); \
} \
} \
} \
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
return head; \
} \
\
static inline void \
M_C(name, _lflist_clear)(M_C(name, _lflist_t) list) \
{ \
m_backoff_t bkoff; \
m_backoff_init(bkoff); \
while (true) { \
M_C(name, _lf_node_t) *node = M_C(name, _lflist_pop)(list, bkoff); \
if (node == NULL) break; \
M_C(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
memory_order_relaxed); \
M_C(name, _slist_clear)(node->list); \
M_MEMORY_DEL(node); \
node = next; \
} \
/* Dummy node to free too */ \
M_C(name, _lf_node_t) *dummy; \
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
M_C(name, _slist_clear)(dummy->list); \
M_MEMORY_DEL(dummy); \
} \
/* System node allocator: request memory to the system.
As such it is a non Lock-Free path. */
#define C_MEMPOOL_DEF_SYSTEM_ALLOC(name, type_t) \
\
static inline M_C(name, _lf_node_t) * \
M_C(name, _alloc_node)(unsigned int initial) \
{ \
M_C(name, _lf_node_t) * node; \
node = M_MEMORY_ALLOC(M_C(name, _lf_node_t)); \
if (M_UNLIKELY(node == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
return NULL; \
} \
atomic_init(&node->next, (M_C(name, _lf_node_t) *) 0); \
atomic_init(&node->cpt, 0UL); \
M_C(name, _slist_init)(node->list); \
for(unsigned i = 0; i < initial; i++) { \
M_C(name, _slist_node_t) *n; \
n = M_MEMORY_ALLOC(M_C(name, _slist_node_t)); \
if (M_UNLIKELY(n == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
return NULL; \
} \
M_C(name, _slist_push)(node->list, n); \
} \
return node; \
} \
/* Concurrent Memory pool
The data structure is the following.
Each thread has its own pool of nodes (local) that only it can
access (it is a singly list). If there is no longer any node in this
pool, it requests a new pool to the lock free queue of pool (group of
nodes). If it fails, it requests a new pool to the system allocator
(and from there it is no longer lock free).
This memory pool can only be lock free if the initial state is
sufficiently dimensionned to avoid calling the system allocator during
the normal processing.
Then each thread pushs its deleted node into another pool of nodes,
where the node is logically deleted (no contain of the node is destroyed
at this point and the node can be freely accessed by other threads).
Once the thread mempool is put to sleep, the age of the pool of logical
deleted nodes is computed and this pool is move to the Lock Free Queue
List of pools to be reclaimed. Then A Garbage Collector is performed
on this Lock Free Queue list to reclaim all pools thare are sufficiently
aged (taking into account the grace period of the pool) to be moved back
to the Lock Free Queue of the free pools.
Each pool of nodes can be in the following state:
* FREE state if it is present in the Lock Free Queue of free pools.
* EMPTY state if it is present in the Lock Free Queue of empty pools
which means that the nodes present in it has been USED directly by a thread,
* TO_BE_RECLAIMED state if it is present in the Lock Free Queue of TBR pools
A pool of nodes will go to the following state:
FREE --> EMPTY --> TO_BE_RECLAIMED
^ |
+----------------------+
The ABA problem is taken into account as a node cannot be reused in the
same queue without performing a full cycle of its state. Moreover
it can only move from TO_BE_RECLAIMED to FREE if and only if a grace
period is finished (and then we are sure that no thread references any
older node).
Each thread has its own backoff structure (with local pseudo-random
generator).
The grace period is detected through a global age counter (ticket)
that is incremented each time a thread is awaken / sleep.
Each thread has its own age that is set to the global ticket on sleep/awaken.
The age of the pool to be reclaimed is also set to this global age counter.
To ensure that the grace period is finished, it tests if all threads
are younger than the age of the pool to be reclaimed.
From a performance point of view, this puts a bottleneck on the global
age counter that is shared and incremented by all threads. However,
the sleep/awaken operations are much less frequent than other operations.
Thus, it shall not have a huge impact on the performance if the user
code is intelligent with the sleep/awaken operations.
As such it won't support more than ULONG_MAX sleep for all threads.
*/
#define C_MEMPOOL_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
\
typedef struct M_C(name, _lfmp_thread_s) { \
M_C(name, _slist_t) free; \
M_C(name, _slist_t) to_be_reclaimed; \
M_CACHELINE_ALIGN(align1, M_C(name, _slist_t), M_C(name, _slist_t)); \
} M_C(name, _lfmp_thread_t); \
\
static inline void \
M_C(name, _lfmp_thread_init)(M_C(name, _lfmp_thread_t) *t) \
{ \
M_C(name, _slist_init)(t->free); \
M_C(name, _slist_init)(t->to_be_reclaimed); \
} \
\
static inline void \
M_C(name, _lfmp_thread_clear)(M_C(name, _lfmp_thread_t) *t) \
{ \
assert(M_C(name, _slist_empty_p)(t->to_be_reclaimed)); \
M_C(name, _slist_clear)(t->free); \
M_C(name, _slist_clear)(t->to_be_reclaimed); \
} \
/* NOTE: once a node is deleted, its data are kept readable until the future GC */
#define C_MEMPOOL_DEF_LF_MEMPOOL(name, type_t) \
\
typedef struct M_C(name, _s) { \
unsigned initial; \
M_C(name, _lfmp_thread_t) *thread_data; \
M_C(name, _lflist_t) free; \
M_C(name, _lflist_t) to_be_reclaimed; \
M_C(name, _lflist_t) empty; \
m_gc_mempool_list_t mempool_node; \
struct m_gc_s *gc_mem; \
} M_C(name, _t)[1]; \
\
/* Garbage collect of the nodes of the mempool on sleep */ \
static inline void \
M_C(name, _int_gc_on_sleep)(m_gc_t gc_mem, m_gc_mempool_list_t *data, \
m_gc_tid_t id, m_gc_ticket_t ticket, m_gc_ticket_t min_ticket) \
{ \
/* Get back the mempool from the node */ \
struct M_C(name, _s) *mempool = \
M_TYPE_FROM_FIELD(struct M_C(name, _s), data, m_gc_mempool_list_t, mempool_node); \
\
/* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \
if (!M_C(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
M_C(name, _lf_node_t) *node; \
/* Get a new empty group of nodes */ \
node = M_C(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \
if (M_UNLIKELY (node == NULL)) { \
/* Fail to get an empty group of node. \
Alloc a new one from the system */ \
node = M_C(name, _alloc_node)(0); \
} \
assert(M_C(name, _slist_empty_p)(node->list)); \
M_C(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \
M_C(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \
} \
\
/* Perform a GC of the freelist of nodes */ \
while (true) { \
M_C(name, _lf_node_t) *node; \
node = M_C(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
min_ticket, gc_mem->thread_data[id].bkoff); \
if (node == NULL) break; \
M_C(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_C(name, _init)(M_C(name, _t) mem, m_gc_t gc_mem, \
unsigned init_node_count, unsigned init_group_count) \
{ \
const unsigned long max_thread = gc_mem->max_thread; \
/* Initialize the thread data of the mempool */ \
mem->thread_data = M_MEMORY_REALLOC(M_C(name, _lfmp_thread_t), NULL, max_thread); \
if (mem->thread_data == NULL) { \
M_MEMORY_FULL(max_thread * sizeof(M_C(name, _lfmp_thread_t))); \
return; \
} \
for(unsigned i = 0; i < max_thread;i++) { \
M_C(name, _lfmp_thread_init)(&mem->thread_data[i]); \
} \
/* Preallocate some group of nodes for the mempool */ \
mem->initial = M_MAX(C_MEMPOOL_MIN_NODE_PER_GROUP, init_node_count); \
M_C(name, _lflist_init)(mem->free, M_C(name, _alloc_node)(init_node_count)); \
M_C(name, _lflist_init)(mem->to_be_reclaimed, M_C(name, _alloc_node)(init_node_count)); \
M_C(name, _lflist_init)(mem->empty, M_C(name, _alloc_node)(0)); \
for(unsigned i = 1; i < init_group_count; i++) { \
M_C(name, _lflist_push)(mem->free, M_C(name, _alloc_node)(init_node_count), \
gc_mem->thread_data[0].bkoff); \
M_C(name, _lflist_push)(mem->empty, M_C(name, _alloc_node)(0), \
gc_mem->thread_data[0].bkoff); \
} \
/* Register the mempool in the GC */ \
mem->mempool_node.gc_on_sleep = M_C(name, _int_gc_on_sleep); \
mem->mempool_node.next = gc_mem->mempool_list; \
gc_mem->mempool_list = &mem->mempool_node; \
mem->gc_mem = gc_mem; \
} \
\
static inline void \
M_C(name, _clear)(M_C(name, _t) mem) \
{ \
const unsigned max_thread = mem->gc_mem->max_thread; \
for(unsigned i = 0; i < max_thread;i++) { \
M_C(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
} \
M_MEMORY_FREE(mem->thread_data); \
mem->thread_data = NULL; \
M_C(name, _lflist_clear)(mem->empty); \
M_C(name, _lflist_clear)(mem->free); \
assert(M_C(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
M_C(name, _lflist_clear)(mem->to_be_reclaimed); \
/* TODO: Unregister from the GC? */ \
} \
\
static inline type_t * \
M_C(name, _new)(M_C(name, _t) mem, m_gc_tid_t id) \
{ \
M_C(name, _slist_node_t) *snode; \
M_C(name, _lf_node_t) *node; \
while (true) { \
/* Fast & likely path where we access the thread pool of nodes */ \
if (M_LIKELY(!M_C(name, _slist_empty_p)(mem->thread_data[id].free))) { \
snode = M_C(name, _slist_pop)(mem->thread_data[id].free); \
return &snode->data; \
} \
/* Request a group node to the freelist of groups */ \
node = M_C(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \
if (M_UNLIKELY (node == NULL)) { \
/* Request a new group to the system. Non Lock Free path */ \
assert(mem->initial > 0); \
node = M_C(name, _alloc_node)(mem->initial); \
assert(!M_C(name, _slist_empty_p)(node->list)); \
} \
M_C(name, _slist_move)(mem->thread_data[id].free, node->list); \
/* Push back the empty group */ \
assert (M_C(name, _slist_empty_p)(node->list)); \
M_C(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_C(name, _del)(M_C(name, _t) mem, type_t *d, m_gc_tid_t id) \
{ \
M_C(name, _slist_node_t) *snode; \
assert( d != NULL); \
snode = M_TYPE_FROM_FIELD(M_C(name, _slist_node_t), d, type_t, data); \
M_C(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
} \
/***********************************************************************/
/* Define the ID of a thread */
typedef int m_gc_tid_t;
/* Define the age of a node */
/* TODO: Compute if sufficient (worst cast ULONG_MAX is 32 bits) */
typedef unsigned long m_gc_ticket_t;
typedef atomic_ulong m_gc_atomic_ticket_t;
/* Define the Linked List of mempools that are registered in the GC */
struct m_gc_s;
typedef struct m_gc_mempool_list_s {
struct m_gc_mempool_list_s *next;
void (*gc_on_sleep)(struct m_gc_s *gc_mem,
struct m_gc_mempool_list_s *data, m_gc_tid_t id,
m_gc_ticket_t ticket, m_gc_ticket_t min_ticket);
void *data;
} m_gc_mempool_list_t;
/* Define the Garbage collector thread data */
typedef struct m_gc_lfmp_thread_s {
m_gc_atomic_ticket_t ticket;
m_backoff_t bkoff;
M_CACHELINE_ALIGN(align1, atomic_ulong, m_backoff_t);
} m_gc_lfmp_thread_t;
/* Define the Garbage collector coordinator */
typedef struct m_gc_s {
m_gc_atomic_ticket_t ticket;
m_gc_tid_t max_thread;
genint_t thread_alloc;
m_gc_lfmp_thread_t *thread_data;
m_gc_mempool_list_t *mempool_list;
} m_gc_t[1];
static inline void
m_gc_init(m_gc_t gc_mem, unsigned long max_thread)
{
assert(gc_mem != NULL);
assert(max_thread > 0 && max_thread < INT_MAX);
atomic_init(&gc_mem->ticket, 0UL);
genint_init(gc_mem->thread_alloc, max_thread);
gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_t, NULL, max_thread);
if (gc_mem->thread_data == NULL) {
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_t));
return;
}
for(unsigned i = 0; i < max_thread;i++) {
atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX);
m_backoff_init(gc_mem->thread_data[i].bkoff);
}
gc_mem->max_thread = max_thread;
gc_mem->mempool_list = NULL;
}
static inline void
m_gc_clear(m_gc_t gc_mem)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) {
m_backoff_clear(gc_mem->thread_data[i].bkoff);
}
M_MEMORY_FREE(gc_mem->thread_data);
gc_mem->thread_data = NULL;
genint_clear(gc_mem->thread_alloc);
}
static inline m_gc_tid_t
m_gc_attach_thread(m_gc_t gc_mem)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
unsigned id = genint_pop(gc_mem->thread_alloc);
return M_ASSIGN_CAST(m_gc_tid_t, id);
}
static inline void
m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
assert(id >= 0 && id < gc_mem->max_thread);
assert(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
genint_push(gc_mem->thread_alloc, id);
}
static inline void
m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
assert(id >= 0 && id < gc_mem->max_thread);
assert(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
m_gc_ticket_t t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1;
atomic_store(&gc_mem->thread_data[id].ticket, t);
}
static inline m_gc_ticket_t
m_gc_int_min_ticket(m_gc_t gc_mem)
{
m_gc_ticket_t min = atomic_load(&gc_mem->thread_data[0].ticket);
for(m_gc_tid_t i = 1; i < gc_mem->max_thread; i++) {
m_gc_ticket_t t = atomic_load(&gc_mem->thread_data[i].ticket);
min = M_MIN(t, min);
}
return min;
}
static inline void
m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id)
{
/* Increase life time of the thread */
m_gc_ticket_t t = atomic_fetch_add(&gc_mem->ticket, 1UL);
atomic_store(&gc_mem->thread_data[id].ticket, t+1);
const m_gc_ticket_t min_ticket = m_gc_int_min_ticket(gc_mem);
/* Iterate over all registered mempools */
m_gc_mempool_list_t *it = gc_mem->mempool_list;
while (it) {
/* Perform a garbage collect of the mempool */
it->gc_on_sleep(gc_mem, it, id, t, min_ticket);
/* Next mempool to scan for GC */
it = it->next;
}
/* Sleep the thread */
atomic_store(&gc_mem->thread_data[id].ticket, ULONG_MAX);
}
/***********************************************************************/
/* */
/* VLA MEMPOOL */
/* */
/***********************************************************************/
C_MEMPOOL_DEF_SINGLY_LIST(m_vlapool, char)
C_MEMPOOL_DEF_LF_QUEUE(m_vlapool, char)
C_MEMPOOL_DEF_SYSTEM_ALLOC(m_vlapool, char)
typedef struct m_vlapool_lfmp_thread_s {
m_vlapool_slist_t to_be_reclaimed;
M_CACHELINE_ALIGN(align1, m_vlapool_slist_t);
} m_vlapool_lfmp_thread_t;
static inline void
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_t *t)
{
m_vlapool_slist_init(t->to_be_reclaimed);
}
static inline void
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_t *t)
{
assert(m_vlapool_slist_empty_p(t->to_be_reclaimed));
m_vlapool_slist_clear(t->to_be_reclaimed);
}
typedef struct m_vlapool_s {
m_vlapool_lflist_t to_be_reclaimed;
m_vlapool_lflist_t empty;
m_vlapool_lfmp_thread_t *thread_data;
m_gc_mempool_list_t mvla_node;
struct m_gc_s *gc_mem;
} m_vlapool_t[1];
/* Garbage collect of the nodes of the vla mempool on sleep */
static inline void
m_vlapool_int_gc_on_sleep(m_gc_t gc_mem, m_gc_mempool_list_t *data,
m_gc_tid_t id, m_gc_ticket_t ticket, m_gc_ticket_t min_ticket)
{
/* Get back the mempool from the node */
struct m_vlapool_s *vlapool =
M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_gc_mempool_list_t, mvla_node);
/* Move the local nodes of the vlapool to be reclaimed to the thread into the global pool */
if (!m_vlapool_slist_empty_p(vlapool->thread_data[id].to_be_reclaimed)) {
m_vlapool_lf_node_t *node;
/* Get a new empty group of nodes */
node = m_vlapool_lflist_pop(vlapool->empty, gc_mem->thread_data[id].bkoff);
if (M_UNLIKELY (node == NULL)) {
/* Fail to get an empty group of node.
Alloc a new one from the system */
node = m_vlapool_alloc_node(0);
}
assert(m_vlapool_slist_empty_p(node->list));
m_vlapool_slist_move(node->list, vlapool->thread_data[id].to_be_reclaimed);
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed);
m_vlapool_lflist_push(vlapool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff);
}
/* Perform a GC of the freelist of nodes */
while (true) {
m_vlapool_lf_node_t *node;
node = m_vlapool_lflist_pop_if(vlapool->to_be_reclaimed,
min_ticket, gc_mem->thread_data[id].bkoff);
if (node == NULL) break;
// No reuse of VLA nodes. Free physically the node back to the system
m_vlapool_slist_clear(node->list);
// Add back the empty group of nodes
m_vlapool_slist_init(node->list);
m_vlapool_lflist_push(vlapool->empty, node, gc_mem->thread_data[id].bkoff);
}
}
static inline void
m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem)
{
const unsigned long max_thread = gc_mem->max_thread;
/* Initialize the thread data of the vlapool */
mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_t, NULL, max_thread);
if (mem->thread_data == NULL) {
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_t));
return;
}
for(unsigned i = 0; i < max_thread;i++) {
m_vlapool_lfmp_thread_init(&mem->thread_data[i]);
}
/* Initialize the lists */
m_vlapool_lflist_init(mem->to_be_reclaimed, m_vlapool_alloc_node(0));
m_vlapool_lflist_init(mem->empty, m_vlapool_alloc_node(0));
/* Register the mempool in the GC */
mem->mvla_node.gc_on_sleep = m_vlapool_int_gc_on_sleep;
mem->mvla_node.next = gc_mem->mempool_list;
gc_mem->mempool_list = &mem->mvla_node;
mem->gc_mem = gc_mem;
}
static inline void
m_vlapool_clear(m_vlapool_t mem)
{
const unsigned max_thread = mem->gc_mem->max_thread;
for(unsigned i = 0; i < max_thread;i++) {
m_vlapool_lfmp_thread_clear(&mem->thread_data[i]);
}
M_MEMORY_FREE(mem->thread_data);
mem->thread_data = NULL;
m_vlapool_lflist_clear(mem->empty);
assert(m_vlapool_lflist_empty_p(mem->to_be_reclaimed));
m_vlapool_lflist_clear(mem->to_be_reclaimed);
/* TODO: Unregister from the GC? */
}
static inline void *
m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size)
{
assert(mem != NULL && mem->gc_mem != NULL);
assert(id >= 0 && id < mem->gc_mem->max_thread);
assert( atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
// Nothing to do with theses parameters yet
(void) mem;
(void) id;
// Ensure the size is big enough to also represent a node
size += offsetof(struct m_vlapool_slist_node_s, data);
// Simply wrap around a system call to get the memory
char *ptr = M_MEMORY_REALLOC(char, NULL, size);
return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data));
}
static inline void
m_vlapool_del(m_vlapool_t mem, void *d, m_gc_tid_t id)
{
assert(mem != NULL && mem->gc_mem != NULL);
assert(id >= 0 && id < mem->gc_mem->max_thread);
assert(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
assert(d != NULL);
// Get back the pointer to a struct m_vlapool_slist_node_s.
d = M_ASSIGN_CAST(void *, M_ASSIGN_CAST(char *, d) - offsetof(struct m_vlapool_slist_node_s, data));
m_vlapool_slist_node_t *snode = M_ASSIGN_CAST(m_vlapool_slist_node_t *, d);
// Push the logicaly free memory into the list of the nodes to be reclaimed.
m_vlapool_slist_push(mem->thread_data[id].to_be_reclaimed, snode);
}
#endif
+61 -42
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Basic Protected Concurrent module over container.
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -29,7 +29,8 @@
#include "m-mutex.h"
#include "m-atomic.h"
/* Define a protected concurrent container and its associated functions.
/* Define a protected concurrent container and its associated functions
based on the given container.
USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */
#define CONCURRENT_DEF(name, ...) \
CONCURRENTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
@@ -45,13 +46,16 @@
(__VA_ARGS__ )))
/* Define a protected concurrent container and its associated functions with Read Preference.
/* Define a protected concurrent container and its associated functions
based on its given container. Operations that perform only read of the container
can be done in parallel.
USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */
#define CONCURRENT_RP_DEF(name, ...) \
CONCURRENTI_RP_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
/********************************** INTERNAL ************************************/
// Deferred evaluation for the oplist definition.
@@ -152,21 +156,13 @@
} \
\
static inline void \
M_C(name, _read_wait)(int *p, const concurrent_t out) \
M_C(name, _read_wait)(const concurrent_t out) \
{ \
assert (out->self == out); \
(void) p; \
m_cond_wait(out->self->there_is_data, out->self->lock); \
} \
\
static inline void \
M_C(name, _read_wait_unlock)(int *p, const concurrent_t out) \
{ \
(void) p; \
m_mutex_unlock (out->self->lock); \
} \
\
static inline void \
M_C(name, _write_lock)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
@@ -524,7 +520,6 @@
CONCURRENTI_CONTRACT(out); \
assert (out_data != NULL); \
bool ret = false; \
int count = 0; \
M_C(name, _read_lock)(out); \
while (true) { \
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
@@ -534,9 +529,9 @@
break; \
} \
if (blocking == false) break; \
M_C(name, _read_wait)(&count, out); \
M_C(name, _read_wait)(out); \
} \
M_C(name, _read_wait_unlock)(&count, out); \
M_C(name, _read_unlock)(out); \
return ret; \
} \
,) \
@@ -602,14 +597,15 @@
// Deferred evaluation for the concurrent definition.
#define CONCURRENTI_RP_DEF(arg) CONCURRENTI_RP_DEF2 arg
// Internal definition.
// Internal definition of RP
#define CONCURRENTI_RP_DEF2(name, type, oplist, concurrent_t, concurrent_it_t) \
\
typedef struct M_C(name, _s) { \
struct M_C(name, _s) *self; \
m_mutex_t lock; \
m_mutex_t read_lock; \
m_cond_t rw_done; \
size_t read_count; \
bool writer_waiting; \
m_cond_t there_is_data; /* condition raised when there is data */ \
type data; \
} concurrent_t[1]; \
@@ -624,10 +620,11 @@
M_C(name, _internal_init)(concurrent_t out) \
{ \
m_mutex_init(out->lock); \
m_mutex_init(out->read_lock); \
m_cond_init(out->rw_done); \
m_cond_init(out->there_is_data); \
out->self = out; \
out->read_count = 0; \
out->writer_waiting = false; \
} \
\
static inline void \
@@ -635,7 +632,7 @@
{ \
assert (out->self == out); \
m_mutex_clear(out->lock); \
m_mutex_clear(out->read_lock); \
m_cond_clear(out->rw_done); \
m_cond_clear(out->there_is_data); \
out->self = NULL; \
} \
@@ -645,12 +642,12 @@
{ \
struct M_C(name, _s) *self = out->self; \
assert (self == out); \
m_mutex_lock (self->read_lock); \
self->read_count ++; \
if (self->read_count == 1) { \
m_mutex_lock (self->lock); \
m_mutex_lock (self->lock); \
while (self->writer_waiting == true) { \
m_cond_wait(self->rw_done, self->lock); \
} \
m_mutex_unlock (self->read_lock); \
self->read_count ++; \
m_mutex_unlock (self->lock); \
} \
\
static inline void \
@@ -658,56 +655,78 @@
{ \
struct M_C(name, _s) *self = out->self; \
assert (self == out); \
m_mutex_lock (self->read_lock); \
m_mutex_lock (self->lock); \
self->read_count --; \
if (self->read_count == 0) { \
m_mutex_unlock (self->lock); \
m_cond_broadcast (self->rw_done); \
} \
m_mutex_unlock (self->read_lock); \
m_mutex_unlock (self->lock); \
} \
\
static inline void \
M_C(name, _write_lock)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
while (out->writer_waiting == true) { \
m_cond_wait(out->rw_done, out->lock); \
} \
out->writer_waiting = true; \
while (out->read_count > 0) { \
m_cond_wait(out->rw_done, out->lock); \
} \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _write_unlock)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
out->writer_waiting = false; \
m_cond_broadcast (out->rw_done); \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _read_wait)(int *p, const concurrent_t out) \
M_C(name, _read_wait)(const concurrent_t out) \
{ \
struct M_C(name, _s) *self = out->self; \
assert (self == out); \
if (*p == 0) { \
m_mutex_unlock (self->read_lock); \
m_mutex_lock (self->lock); \
*p = 1; \
} else { \
m_cond_wait(self->there_is_data, self->lock); \
m_mutex_lock (out->self->lock); \
self->read_count --; \
if (self->read_count == 0) { \
m_cond_broadcast (self->rw_done); \
} \
m_cond_wait(self->there_is_data, self->lock); \
while (self->writer_waiting == true) { \
m_cond_wait(self->rw_done, self->lock); \
} \
self->read_count ++; \
m_mutex_unlock (out->self->lock); \
} \
\
static inline void \
M_C(name, _read_wait_unlock)(int *p, const concurrent_t out) \
M_C(name, _write_wait)(concurrent_t out) \
{ \
m_mutex_unlock (*p == 0 ? out->self->read_lock : out->self->lock); \
} \
\
static inline void \
M_C(name, _write_wait)(const concurrent_t out) \
{ \
m_cond_wait(out->self->there_is_data, out->self->lock); \
m_mutex_lock (out->lock); \
out->writer_waiting = false; \
m_cond_broadcast (out->rw_done); \
m_cond_wait(out->there_is_data, out->lock); \
while (out->writer_waiting == true) { \
m_cond_wait(out->rw_done, out->lock); \
} \
out->writer_waiting = true; \
while (out->read_count > 0) { \
m_cond_wait(out->rw_done, out->lock); \
} \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _write_signal)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
m_cond_broadcast(out->there_is_data); \
m_mutex_unlock (out->lock); \
} \
\
CONCURRENTI_DEF_FUNC(name, type, oplist, concurrent_t, concurrent_it_t)
+102 -22
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Extended Pre-processing macros module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -37,7 +37,7 @@
#define M_CORE_VERSION_MAJOR 0
#define M_CORE_VERSION_MINOR 2
#define M_CORE_VERSION_PATCHLEVEL 0
#define M_CORE_VERSION_PATCHLEVEL 2
/* By default, always use stdio. Can be turned off in specific environement if needed */
#ifndef M_USE_STDIO
@@ -78,20 +78,28 @@
/* Define the exclusion size so that 2 atomic variables can be in
separate cache line. This prevents false sharing to occur within the
CPU cache. */
CPU cache line. */
#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
# define M_ALIGN_FOR_CACHELINE_EXCLUSION 128
#else
# define M_ALIGN_FOR_CACHELINE_EXCLUSION 64
#endif
/* Deprecated attribute for function */
/* Deprecated attribute for a function */
#if defined(__GNUC__) && __GNUC__ >= 4
#define M_ATTR_DEPRECATED __attribute__((deprecated))
#else
#define M_ATTR_DEPRECATED
#endif
/* Extension attribute to silent warnings on extensions */
#if defined(__GNUC__)
#define M_ATTR_EXTENSION __extension__
#else
#define M_ATTR_EXTENSION
#endif
/***************************************************************/
/****************** Preprocessing Times Macro ******************/
/***************************************************************/
@@ -1465,6 +1473,7 @@ M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, )
(y) = _tmp; \
} while (0)
/* Check if 'n' is assignable to an object of type 'type'.
It is as if we create a temporary of type 'type' and assign 'n' to it.
Return the object 'n' if it is possible.
@@ -1477,6 +1486,7 @@ M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, )
# define M_ASSIGN_CAST(type, n) static_cast<type>(n)
#endif
/* Cast 'n' of type 'type*' into 'type const*'.
This is like (type const*)p but safer as the type of 'n' is checked,
and more robust for double arrays type.
@@ -1492,10 +1502,12 @@ M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, )
# define M_CONST_CAST(type, n) const_cast<type*>(n)
#endif
/*
* From a pointer to a 'field_type' 'field' of a 'type'structure,
* return pointer to the structure.
* NOTE: Cast Inside!
* From a pointer to the 'field' field of type 'field_type' of a 'type' structure,
* return the pointer to the structure.
* Used to handle intrusive structure.
* NOTE: Use an ASSIGN_CAST to make the cast a little bit safer.
*/
#define M_TYPE_FROM_FIELD(type, ptr, field_type, field) \
((type *)(void*)( (char *)M_ASSIGN_CAST(field_type*, (ptr)) - offsetof(type, field) ))
@@ -1504,6 +1516,26 @@ M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, )
((type const *)(const void*)( (const char *)M_ASSIGN_CAST(field_type const *, (ptr)) - offsetof(type const, field) ))
/* Use to generate a dummy alignment field for cache alignment within a structure
Take the name of the dummy field, and a list of the type of the fields that previously fill in the structure.
*/
#define M_APPLY_SIZEOF(id) sizeof (id)
#define M_ADD_SIZE(a,b) a + b
#define M_CACHELINE_ALIGN(name, ...) \
char name[M_ALIGN_FOR_CACHELINE_EXCLUSION > M_REDUCE(M_APPLY_SIZEOF, M_ADD_SIZE, __VA_ARGS__) \
? M_ALIGN_FOR_CACHELINE_EXCLUSION - M_REDUCE(M_APPLY_SIZEOF, M_ADD_SIZE, __VA_ARGS__) : 1]
/* C++ doesn't support flexible array within a structure.
Let's define at least one element for an array. */
#ifdef __cplusplus
# define M_MIN_FLEX_ARRAY_SIZE 1
#else
# define M_MIN_FLEX_ARRAY_SIZE
#endif
/************************************************************/
/********************* HASH selection ***********************/
/************************************************************/
@@ -1649,7 +1681,7 @@ m_core_hash (const void *str, size_t length)
uint32_t hash32 = 2166136261U ^ M_HASH_SEED;
const uint8_t *p = (const uint8_t *)str;
assert (str != NULL);
assert (str != NULL || length == 0);
assert ( (( (uintptr_t)p & (sizeof(uint32_t)-1) ) == 0) || (length <= sizeof(uint32_t)));
while (length >= 2*sizeof(uint32_t)) {
@@ -1682,7 +1714,7 @@ m_core_hash (const void *str, size_t length)
uint64_t hash64 = 14695981039346656037ULL ^ M_HASH_SEED;
const uint8_t *p = M_ASSIGN_CAST(const uint8_t *, str);
assert (str != NULL);
assert (str != NULL || length == 0);
assert ( (( (uintptr_t)p & (sizeof(uint64_t)-1) ) == 0) || (length <= sizeof(uint32_t)));
while (length >= 2*sizeof(uint64_t)) {
@@ -1763,6 +1795,7 @@ m_core_hash (const void *str, size_t length)
#define M_OPLIST_OPLIST(a) ,a,
#define M_SORT_SORT(a) ,a,
#define M_UPDATE_UPDATE(a) ,a,
#define M_UPDATE_TYPE_UPDATE_TYPE(a) ,a,
#define M_SPLICE_BACK_SPLICE_BACK(a) ,a,
#define M_SPLICE_AT_SPLICE_AT(a) ,a,
#define M_IT_TYPE_IT_TYPE(a) ,a,
@@ -1836,6 +1869,7 @@ m_core_hash (const void *str, size_t length)
#define M_GET_EQUAL(...) M_GET_METHOD(EQUAL, M_EQUAL_DEFAULT, __VA_ARGS__)
#define M_GET_CMP(...) M_GET_METHOD(CMP, M_CMP_DEFAULT, __VA_ARGS__)
#define M_GET_UPDATE(...) M_GET_METHOD(UPDATE, M_SET_DEFAULT, __VA_ARGS__)
#define M_GET_UPDATE_TYPE(...) M_GET_METHOD(UPDATE_TYPE, M_NO_DEFAULT, __VA_ARGS__)
#define M_GET_TYPE(...) M_GET_METHOD(TYPE, M_NO_DEFAULT, __VA_ARGS__)
#define M_GET_SUBTYPE(...) M_GET_METHOD(SUBTYPE, M_NO_DEFAULT, __VA_ARGS__)
#define M_GET_OPLIST(...) M_GET_METHOD(OPLIST, (), __VA_ARGS__)
@@ -2070,12 +2104,16 @@ m_core_hash (const void *str, size_t length)
HASH(M_HASH_DEFAULT), SWAP(M_SWAP_DEFAULT) )
#endif
/* Default oplist for standard types for pointers.
*/
#define M_PTR_OPLIST \
(INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_DEFAULT), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
SWAP(M_SWAP_DEFAULT) )
/* Default oplist for complex objects with "classic" names for methods.
*/
#define M_CLASSIC_OPLIST(name) ( \
INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
@@ -2348,27 +2386,69 @@ m_core_hash (const void *str, size_t length)
// * abort
#ifndef M_MEMORY_FULL
#define M_MEMORY_FULL(size) do { \
fprintf(stderr, "ERROR(M*LIB): Can not allocate memory in function %s of file %s:%d (%zu bytes).\n", \
__func__, __FILE__, __LINE__, (size_t) size); \
abort(); \
} while (0)
#endif
#ifndef M_INIT_FAILURE
#define M_INIT_FAILURE() do { \
fprintf(stderr, "ERROR: Can not initialize data in (%s:%s:%d).\n", \
__FILE__, __func__, __LINE__); \
fprintf(stderr, "ERROR(M*LIB): Cannot allocate %zu bytes of memory at (%s:%s:%d).\n", \
(size_t) (size), __func__, __FILE__, __LINE__); \
abort(); \
} while (0)
#endif
#ifndef M_ASSERT_INIT
#define M_ASSERT_INIT(expr) do { \
#define M_ASSERT_INIT(expr, object) { \
if (!(expr)) { \
fprintf(stderr, "ERROR: Can not initialize data in (%s:%s:%d): %s\n", \
__FILE__, __func__, __LINE__, #expr); \
fprintf(stderr, "ERROR(M*LIB): Cannot initialize %s at (%s:%s:%d): %s\n", \
(object), __FILE__, __func__, __LINE__, #expr); \
abort(); \
} } while (0)
#endif
/************************************************************/
/******************* Exponential Backoff ********************/
/************************************************************/
#ifndef M_BACKOFF_MAX_COUNT
#define M_BACKOFF_MAX_COUNT 6
#endif
typedef struct m_backoff_s {
unsigned int count;
unsigned int seed;
} m_backoff_t[1];
static inline void
m_backoff_init(m_backoff_t backoff)
{
backoff->count = 0;
backoff->seed = rand();
}
static inline void
m_backoff_reset(m_backoff_t backoff)
{
backoff->count = 0;
}
static inline void
m_backoff_wait(m_backoff_t backoff)
{
volatile int x = 0;
/* Cheap but fast pseudo random */
backoff->seed = backoff->seed * 34721 + 17449;
const unsigned int mask = (1U << backoff->count) -1;
const unsigned int count = mask & (backoff->seed >> 8);
/* Active sleep */
for(unsigned int i = 0; i <= count; i++)
x = 0;
(void) x;
/* Increment count for next step if needed */
backoff->count += (backoff->count < M_BACKOFF_MAX_COUNT);
}
static inline void
m_backoff_clear(m_backoff_t backoff)
{
// Nothing to do
(void) backoff;
}
#endif
+2 -10
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - DEQUE module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -46,14 +46,6 @@
#define DEQUEUI_DEFAULT_SIZE 8
/* C++ doesn't support flexible array within a structure.
Let's define at least one element. */
#ifdef __cplusplus
# define DEQUEI_MIN_ARRAY_SIZE 1
#else
# define DEQUEI_MIN_ARRAY_SIZE
#endif
#define DEQUEI_CONTRACT(d) do { \
assert ((d) != NULL); \
assert ((d)->default_size >= DEQUEUI_DEFAULT_SIZE); \
@@ -74,7 +66,7 @@
typedef struct M_C(name, _node_s) { \
ILIST_INTERFACE(M_C(name, _node_list), struct M_C(name, _node_s)); \
size_t size; \
type data[DEQUEI_MIN_ARRAY_SIZE]; \
type data[M_MIN_FLEX_ARRAY_SIZE]; \
} node_t; \
\
/* Each node is allocated with a variable size (so we use \
+79 -68
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - DICTIONARY Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -519,6 +519,13 @@
M_C(name, _list_pair_it_set)(it->list_it, ref->list_it); \
} \
\
static inline void \
M_C(name, _it_end)(dict_it_t it, const dict_t d) \
{ \
DICTI_CONTRACT(name, d); \
M_C(name, _array_list_pair_it_end)(it->array_it, d->table); \
} \
\
static inline bool \
M_C(name, _end_p)(const dict_it_t it) \
{ \
@@ -829,6 +836,7 @@
IT_TYPE(M_C(name, _it_t)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name, _it_set)), \
IT_END(M_C(name,_it_end)), \
IT_END_P(M_C(name,_end_p)), \
IT_LAST_P(M_C(name,_last_p)), \
IT_NEXT(M_C(name,_next)), \
@@ -880,6 +888,7 @@
IT_TYPE(M_C(name, _it_t)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name, _it_set)), \
IT_END(M_C(name,_it_end)), \
IT_END_P(M_C(name,_end_p)), \
IT_LAST_P(M_C(name,_last_p)), \
IT_NEXT(M_C(name,_next)), \
@@ -935,16 +944,18 @@ typedef enum {
#define DICTI_OA_DEF_P1(args) DICTI_OA_DEF_P2 args
#define DICTI_OA_DEF_P2(name, key_type, key_oplist, value_type, value_oplist) \
DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, \
M_GET_OOR_EQUAL key_oplist, M_GET_OOR_SET key_oplist, \
0.2, 0.7, M_C(name,_t), M_C(name, _it_t) )
0.2, 0.7, M_C(name,_t), M_C(name, _it_t) )
#define DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, oor_equal_p, oor_set, coeff_down, coeff_up, dict_t, dict_it_t) \
#define DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, coeff_down, coeff_up, dict_t, dict_it_t) \
\
typedef struct M_C(name, _pair_s) { \
key_type key; \
value_type value; \
} M_C(name, _pair_t); \
\
typedef key_type M_C(name, _key_type_t); \
typedef value_type M_C(name, _value_type_t); \
\
/* NOTE: We don't want a real oplist for this type */ \
ARRAY_DEF(M_C(name, _array_pair), M_C(name, _pair_t), \
(INIT(M_NOTHING_DEFAULT), SET(M_MEMCPY_DEFAULT), \
@@ -981,11 +992,11 @@ typedef enum {
M_C(name,_int_limit)(dict, DICTI_INITIAL_SIZE); \
dict->data = M_CALL_REALLOC(key_oplist, M_C(name, _pair_t), NULL, DICTI_INITIAL_SIZE); \
if (dict->data == NULL) { \
M_MEMORY_FULL(sizeof (M_C(name, _pair_t)) * DICTI_INITIAL_SIZE); \
M_MEMORY_FULL(sizeof (M_C(name, _pair_t)) * DICTI_INITIAL_SIZE); \
return ; \
} \
for(size_t i = 0; i < DICTI_INITIAL_SIZE; i++) { \
oor_set(dict->data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, dict->data[i].key, DICTI_OA_EMPTY); \
} \
DICTI_OA_CONTRACT(dict); \
} \
@@ -995,8 +1006,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
for(size_t i = 0; i <= dict->mask; i++) { \
if (!oor_equal_p(dict->data[i].key, DICTI_OA_EMPTY) \
&& !oor_equal_p(dict->data[i].key, DICTI_OA_DELETED)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, DICTI_OA_EMPTY) \
&& !M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, DICTI_OA_DELETED)) { \
M_CALL_CLEAR(key_oplist, dict->data[i].key); \
M_CALL_CLEAR(value_oplist, dict->data[i].value); \
} \
@@ -1012,8 +1023,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: Key can not be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
@@ -1022,7 +1033,7 @@ typedef enum {
/* Random access, and probably cache miss */ \
if (M_LIKELY (M_CALL_EQUAL(key_oplist, data[p].key, key)) ) \
return &data[p].value; \
else if (M_LIKELY (oor_equal_p (data[p].key, DICTI_OA_EMPTY)) ) \
else if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) ) \
return NULL; \
\
/* Unlikely case */ \
@@ -1032,7 +1043,7 @@ typedef enum {
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) \
return &data[p].value; \
assert (s <= dict->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
\
return NULL; \
} \
@@ -1052,8 +1063,8 @@ typedef enum {
size_t empty = 0; \
size_t del = 0; \
for(size_t i = 0 ; i <= h->mask ; i++) { \
empty += oor_equal_p(data[i].key, DICTI_OA_EMPTY); \
del += oor_equal_p(data[i].key, DICTI_OA_DELETED); \
empty += M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY); \
del += M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED); \
} \
assert(del == 0); \
assert(empty + h->count == h->mask + 1); \
@@ -1078,7 +1089,7 @@ typedef enum {
\
/* First mark the extended space as empty */ \
for(size_t i = oldSize ; i < newSize; i++) \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
\
/* Then let's rehash all the entries in their **exact** position. \
@@ -1091,10 +1102,10 @@ typedef enum {
const size_t mask = (newSize -1); \
\
for(size_t i = 0 ; i < oldSize; i++) { \
if (!oor_equal_p(data[i].key, DICTI_OA_DELETED) && !oor_equal_p(data[i].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED) && !M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) { \
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
if (p != i) { \
if (M_LIKELY (oor_equal_p(data[p].key, DICTI_OA_EMPTY) || oor_equal_p(data[p].key, DICTI_OA_DELETED))) { \
if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) || M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED))) { \
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} else { \
@@ -1102,10 +1113,10 @@ typedef enum {
M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
} \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
} else { \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
} \
\
@@ -1116,15 +1127,15 @@ typedef enum {
M_C(name, _pair_t) *item = M_C(name, _array_pair_get)(tmp, i); \
size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \
/* NOTE: since the first pass, the bucket might be free now */ \
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
assert (s <= h->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
} \
/* FIXME: How can I use INIT_MOVE without garbaging the array? ==> POP_INIT_MOVE ! */ \
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
M_CALL_INIT_SET(value_oplist, data[p].value, item->value); \
} \
\
@@ -1145,8 +1156,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: key can not be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
@@ -1157,9 +1168,9 @@ typedef enum {
M_CALL_SET(value_oplist, data[p].value, value); \
return; \
} \
if (M_UNLIKELY (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) ) { \
if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) ) { \
size_t delPos = -1; \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED)) delPos = p; \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) delPos = p; \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
@@ -1168,8 +1179,8 @@ typedef enum {
return; \
} \
assert (s <= dict->mask); \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
if (delPos != (size_t) -1) { \
p = delPos; \
dict->count_delete --; \
@@ -1199,8 +1210,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: key can not be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
@@ -1209,9 +1220,9 @@ typedef enum {
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \
return &data[p].value; \
} \
if (M_UNLIKELY (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) ) { \
if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) ) { \
size_t delPos = -1; \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED)) delPos = p; \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) delPos = p; \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
@@ -1219,8 +1230,8 @@ typedef enum {
return &data[p].value; \
} \
assert (s <= dict->mask); \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
if (delPos != (size_t) -1) { \
p = delPos; \
dict->count_delete --; \
@@ -1262,15 +1273,15 @@ typedef enum {
\
/* Pass 1: scan lower entries, and move them if needed */ \
for(size_t i = 0; i < newSize; i++) { \
if (oor_equal_p(data[i].key, DICTI_OA_EMPTY)) \
if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) \
continue; \
if (oor_equal_p(data[i].key, DICTI_OA_DELETED)) { \
oor_set(data[i].key, DICTI_OA_EMPTY); \
if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED)) { \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
continue; \
} \
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
if (p != i) { \
if (oor_equal_p(data[p].key, DICTI_OA_EMPTY) || oor_equal_p(data[p].key, DICTI_OA_DELETED)) { \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) || M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) { \
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} else { \
@@ -1278,21 +1289,21 @@ typedef enum {
M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
} \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
} \
/* Pass 2: scan upper entries and move them back */ \
for(size_t i = newSize; i < oldSize; i++) { \
if (!oor_equal_p(data[i].key, DICTI_OA_DELETED) \
&& !oor_equal_p(data[i].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED) \
&& !M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) { \
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
assert (p < i); \
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
assert (s <= h->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
} \
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
@@ -1302,12 +1313,12 @@ typedef enum {
for(size_t i = 0; i < M_C(name, _array_pair_size)(tmp); i++) { \
M_C(name, _pair_t) *item = M_C(name, _array_pair_get)(tmp, i); \
size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
assert (s <= h->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
} \
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
M_CALL_INIT_SET(value_oplist, data[p].value, item->value); \
@@ -1331,28 +1342,28 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: key can't be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
\
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
size_t p = M_CALL_HASH(key_oplist, key) & mask; \
\
/* Random access, and probably cache miss */ \
if (M_UNLIKELY (!M_CALL_EQUAL(key_oplist, data[p].key, key)) ) { \
if (oor_equal_p (data[p].key, DICTI_OA_EMPTY)) \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) \
return false; \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
if (oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) \
return false; \
assert (s <= dict->mask); \
} while (!M_CALL_EQUAL(key_oplist, data[p].key, key)); \
} \
M_CALL_CLEAR(key_oplist, data[p].key); \
M_CALL_CLEAR(value_oplist, data[p].value); \
oor_set (data[p].key, DICTI_OA_DELETED); \
M_CALL_OOR_SET(key_oplist, data[p].key, DICTI_OA_DELETED); \
assert (dict->count >= 1); \
dict->count--; \
if (M_UNLIKELY (dict->count < dict->lower_limit)) { \
@@ -1392,10 +1403,10 @@ typedef enum {
return ; \
} \
for(size_t i = 0; i <= org->mask; i++) { \
if (oor_equal_p(org->data[i].key, DICTI_OA_EMPTY)) { \
oor_set(map->data[i].key, DICTI_OA_EMPTY); \
} else if (oor_equal_p(org->data[i].key, DICTI_OA_DELETED)) { \
oor_set(map->data[i].key, DICTI_OA_DELETED); \
if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, DICTI_OA_EMPTY)) { \
M_CALL_OOR_SET(key_oplist, map->data[i].key, DICTI_OA_EMPTY); \
} else if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, DICTI_OA_DELETED)) { \
M_CALL_OOR_SET(key_oplist, map->data[i].key, DICTI_OA_DELETED); \
} else { \
M_CALL_INIT_SET(key_oplist, map->data[i].key, org->data[i].key); \
M_CALL_INIT_SET(value_oplist, map->data[i].value, org->data[i].value); \
@@ -1465,8 +1476,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(d); \
for(size_t i = 0; i <= d->mask; i++) { \
if (!oor_equal_p(d->data[i].key, DICTI_OA_EMPTY) \
&& !oor_equal_p(d->data[i].key, DICTI_OA_DELETED)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
&& !M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) { \
M_CALL_CLEAR(key_oplist, d->data[i].key); \
M_CALL_CLEAR(value_oplist, d->data[i].value); \
} \
@@ -1479,7 +1490,7 @@ typedef enum {
d->data, DICTI_INITIAL_SIZE); \
assert(d->data != NULL); \
for(size_t i = 0; i <= d->mask; i++) { \
oor_set(d->data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, d->data[i].key, DICTI_OA_EMPTY); \
} \
DICTI_OA_CONTRACT(d); \
} \
@@ -1491,8 +1502,8 @@ typedef enum {
assert (it != NULL); \
it->dict = d; \
size_t i = 0; \
while ((oor_equal_p (d->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (d->data[i].key, DICTI_OA_DELETED)) \
while ((M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) \
&& i <= d->mask) { \
i++; \
} \
@@ -1516,8 +1527,8 @@ typedef enum {
assert (it != NULL); \
it->dict = d; \
size_t i = d->mask; \
while ((oor_equal_p (d->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (d->data[i].key, DICTI_OA_DELETED)) \
while ((M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) \
&& i <= d->mask) { \
i--; \
} \
@@ -1548,8 +1559,8 @@ typedef enum {
DICTI_OA_CONTRACT (it->dict); \
size_t i = it->index + 1; \
while (i <= it->dict->mask && \
(oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED))) { \
(M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED))) { \
i++; \
} \
it->index = i; \
@@ -1563,8 +1574,8 @@ typedef enum {
/* if index was 0, the operation will overflow, and stops the loop */ \
size_t i = it->index - 1; \
while (i <= it->dict->mask && \
(oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED))) { \
(M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED))) { \
i--; \
} \
it->index = i; \
@@ -1596,8 +1607,8 @@ typedef enum {
DICTI_OA_CONTRACT (it -> dict); \
const size_t i = it->index; \
assert (i <= it->dict->mask); \
assert (!oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED)); \
return &it->dict->data[i]; \
} \
\
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - GENINT module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+7 -7
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Intrusive List module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -339,23 +339,23 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _it_last)(list_it_t it, list_t list) \
M_C(name, _it_last)(list_it_t it, list_t const list) \
{ \
ILISTI_CONTRACT(name, list); \
assert (it != NULL); \
it->head = &list->name; \
it->head = list->name.next->prev; \
it->current = list->name.prev; \
it->next = &list->name; \
it->next = it->head; \
it->previous = list->name.prev->prev; \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
static inline void \
M_C(name, _it_end)(list_it_t it, list_t list) \
M_C(name, _it_end)(list_it_t it, list_t const list) \
{ \
assert (it != NULL && list != NULL); \
it->head = &list->name; \
it->current = &list->name; \
it->head = list->name.next->prev; \
it->current = it->head; \
it->next = list->name.next; \
it->previous = list->name.prev; \
ILISTI_NODE_CONTRACT(it->current); \
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - INTRUSIVE SHARED PTR Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - LIST module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - MEMPOOL module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+9 -9
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin Mutex & Thread wrapper
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -42,7 +42,7 @@ typedef thrd_t m_thread_t[1];
static inline void m_mutex_init(m_mutex_t m)
{
int rc = mtx_init(m, mtx_plain);
M_ASSERT_INIT (rc == 0);
M_ASSERT_INIT (rc == 0, "mutex");
}
static inline void m_mutex_clear(m_mutex_t m)
@@ -63,7 +63,7 @@ static inline void m_mutex_unlock(m_mutex_t m)
static inline void m_cond_init(m_cond_t c)
{
int rc = cnd_init(c);
M_ASSERT_INIT (rc == 0);
M_ASSERT_INIT (rc == 0, "conditional variable");
}
static inline void m_cond_clear(m_cond_t c)
@@ -89,7 +89,7 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
{
int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (rc == thrd_success);
M_ASSERT_INIT (rc == thrd_success, "thread");
}
static inline void m_thread_join(m_thread_t t)
@@ -192,7 +192,7 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, arg, 0, NULL);
M_ASSERT_INIT (*t != NULL);
M_ASSERT_INIT (*t != NULL, "thread");
}
static inline void m_thread_join(m_thread_t t)
@@ -213,7 +213,7 @@ static inline bool m_thread_sleep(unsigned long long usec)
LARGE_INTEGER ft;
ft.QuadPart = -(10ULL*usec);
HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
M_ASSERT_INIT (hd != NULL);
M_ASSERT_INIT (hd != NULL, "timer");
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
CloseHandle(hd);
@@ -266,7 +266,7 @@ typedef pthread_t m_thread_t[1];
static inline void m_mutex_init(m_mutex_t m)
{
int _rc = pthread_mutex_init(m, NULL);
M_ASSERT_INIT (_rc == 0);
M_ASSERT_INIT (_rc == 0, "mutex");
}
static inline void m_mutex_clear(m_mutex_t m)
@@ -293,7 +293,7 @@ static inline void m_mutexi_lazy_lock(m_mutex_t m)
static inline void m_cond_init(m_cond_t c)
{
int _rc = pthread_cond_init(c, NULL);
M_ASSERT_INIT (_rc == 0);
M_ASSERT_INIT (_rc == 0, "conditional variable");
}
static inline void m_cond_clear(m_cond_t c)
@@ -319,7 +319,7 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (_rc == 0);
M_ASSERT_INIT (_rc == 0, "thread");
}
static inline void m_thread_join(m_thread_t t)
+2 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic priority queue module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -344,5 +344,6 @@
, /* No EQUAL */ \) \
// TODO: set all & remove all function
#endif
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - RED BLACK TREE module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SHARED Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -305,7 +305,7 @@ static inline int sharedi_integer_cref(int *p) { return *p; }
typedef struct M_C(name, _atype_s) { \
atomic_uint cpt; \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) +sizeof(atomic_uint) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) -sizeof(atomic_uint) : 1]; \
M_CACHELINE_ALIGN(align, type, atomic_uint); \
} M_C(name, _atype_t); \
\
typedef struct M_C(name, _s) { \
+3 -3
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SNAPSHOT Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -120,7 +120,7 @@
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _aligned_type_s) { \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
M_CACHELINE_ALIGN(align, type); \
} M_C(name, _aligned_type_t); \
\
typedef struct M_C(name, _s) { \
@@ -487,7 +487,7 @@ static inline void snapshot_mrsw_int_read_end(snapshot_mrsw_int_t s, unsigned in
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _aligned_type_s) { \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
M_CACHELINE_ALIGN(align, type); \
} M_C(name, _aligned_type_t); \
\
typedef struct M_C(name, _s) { \
+528 -294
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - VARIANT module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+44 -32
View File
@@ -1,7 +1,7 @@
/*
* M*LIB / WORKER - Extra worker interface
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -25,13 +25,22 @@
#ifndef MSTARLIB_WORKER_H
#define MSTARLIB_WORKER_H
#ifndef WORKER_DISABLE
/* The User Code can define M_USE_WORKER to 0 to disable the use of workers.
The macros / functions are then defined to only use one core.
By default, the behavior is to use workers.
*/
#ifndef M_USE_WORKER
# define M_USE_WORKER 1
#endif
#if M_USE_WORKER
#include "m-atomic.h"
#include "m-buffer.h"
#include "m-mutex.h"
/* Needed header for detection of how many core on the system */
/* Include needed system header for detection of how many core on the system */
#if defined(_WIN32)
# include <windows.h>
#elif (defined(__APPLE__) && defined(__MACH__)) \
@@ -39,7 +48,7 @@
|| defined(__NetBSD__) || defined(__OpenBSD__)
# include <sys/param.h>
# include <sys/sysctl.h>
# define WORKER_USE_SYSCTL
# define M_USE_WORKER_SYSCTL 1
#else
# include <unistd.h>
#endif
@@ -53,29 +62,35 @@
if C++, it will use Lambda function (and std::function) instead
(It doesn't support pre-C++11 compiler).
Otherwise go with nested function for the MACRO version.
Otherwise go with nested function (GCC) for the MACRO version.
This behavior can be overriden by User Code by defining to 1 or 0 the
following macros:
* M_USE_WORKER_CPP_FUNCTION
* M_USE_WORKER_CLANG_BLOCK
*/
#if defined(__cplusplus) && !defined(WORKER_USE_CPP_FUNCTION)
# define WORKER_USE_CPP_FUNCTION 1
#if defined(__cplusplus) && !defined(M_USE_WORKER_CPP_FUNCTION)
# define M_USE_WORKER_CPP_FUNCTION 1
# include <functional>
#elif defined(__has_extension) && !defined(WORKER_USE_CLANG_BLOCK)
#elif defined(__has_extension) && !defined(M_USE_WORKER_CLANG_BLOCK)
# if __has_extension(blocks)
# define WORKER_USE_CLANG_BLOCK 1
# define M_USE_WORKER_CLANG_BLOCK 1
# endif
#endif
#ifndef WORKER_USE_CLANG_BLOCK
# define WORKER_USE_CLANG_BLOCK 0
#ifndef M_USE_WORKER_CLANG_BLOCK
# define M_USE_WORKER_CLANG_BLOCK 0
#endif
#ifndef WORKER_USE_CPP_FUNCTION
# define WORKER_USE_CPP_FUNCTION 0
#ifndef M_USE_WORKER_CPP_FUNCTION
# define M_USE_WORKER_CPP_FUNCTION 0
#endif
/* Control that not both options are selected at the same time.
Note: there are not really incompatible, but if we use C++ we shall go to
lambda directly (there is no need to support blocks)! */
#if WORKER_USE_CLANG_BLOCK && WORKER_USE_CPP_FUNCTION
# error WORKER_USE_CPP_FUNCTION and WORKER_USE_CLANG_BLOCK are both defined (not supported).
lambda directly (there is no need to support blocks). */
#if M_USE_WORKER_CLANG_BLOCK && M_USE_WORKER_CPP_FUNCTION
# error M_USE_WORKER_CPP_FUNCTION and M_USE_WORKER_CLANG_BLOCK are both defined. This is not supported.
#endif
/* This type defines a work order */
@@ -83,10 +98,10 @@ typedef struct work_order_s {
struct worker_sync_s *block;
void * data;
void (*func) (void *data);
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
void (^blockFunc)(void *data);
#endif
#if WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CPP_FUNCTION
std::function<void(void*)> function;
#endif
} worker_order_t;
@@ -95,7 +110,7 @@ typedef struct work_order_s {
* * MACRO to be used to send an empty order to stop the thread
* * MACRO to complete the not-used fields
*/
#if WORKER_USE_CLANG_BLOCK || WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CLANG_BLOCK || M_USE_WORKER_CPP_FUNCTION
# define WORKER_EMPTY_ORDER { NULL, NULL, NULL, NULL }
# define WORKER_EXTRA_ORDER , NULL
#else
@@ -146,7 +161,7 @@ workeri_get_cpu_count(void)
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#elif defined(WORKER_USE_SYSCTL)
#elif defined(M_USE_WORKER_SYSCTL)
int nm[2];
int count = 0;
size_t len = sizeof (count);
@@ -167,13 +182,13 @@ workeri_exec(worker_order_t *w)
{
assert (w!= NULL && w->block != NULL);
//printf ("Starting thread with data %p\n", w->data);
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
//printf ("Running %s f=%p b=%p\n", (w->func == NULL) ? "Blocks" : "Function", w->func, w->blockFunc);
if (w->func == NULL)
w->blockFunc(w->data);
else
#endif
#if WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CPP_FUNCTION
//printf ("Running %s f=%p b=%p\n", (w->function == NULL) ? "Lambda" : "Function", w->func, w->blockFunc);
if (w->function)
w->function(w->data);
@@ -291,7 +306,7 @@ worker_spawn(worker_sync_t block, void (*func)(void *data), void *data)
(*func) (data);
}
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
/* Spawn or not the given work order to workers,
or do it ourself if no worker is available */
static inline void
@@ -310,7 +325,7 @@ worker_spawn_block(worker_sync_t block, void (^func)(void *data), void *data)
}
#endif
#if WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CPP_FUNCTION
/* Spawn or not the given work order to workers,
or do it ourself if no worker is available */
static inline void
@@ -377,12 +392,12 @@ worker_count(worker_t g)
'input' is the list of input variables of the 'core' block within "( )"
'output' is the list of output variables of the 'core' block within "( )"
Output variables are only available after a synchronisation block. */
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
#define WORKER_SPAWN(_block, _input, _core, _output) \
WORKER_DEF_DATA(_input, _output) \
WORKER_DEF_SUBBLOCK(_input, _output, _core) \
worker_spawn_block ((_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
#elif WORKER_USE_CPP_FUNCTION
#elif M_USE_WORKER_CPP_FUNCTION
// TODO: Explicit pass all arguments by reference.
#define WORKER_SPAWN(_block, _input, _core, _output) \
worker_spawn_function ((_block), [&](void *param) {(void)param ; _core } , NULL)
@@ -452,12 +467,9 @@ worker_count(worker_t g)
M_MAP(WORKER_PROPAGATE_SINGLE_OUTPUT, __VA_ARGS__)
#else /* WORKER_DISABLE */
#else /* M_USE_WORKER */
/* User has defined WORKER_DISABLE to disable the use of workers.
Define empty types and empty functions
*/
/* Define empty types and empty functions to not use any worker */
typedef struct worker_block_s {
int x;
@@ -475,6 +487,6 @@ typedef struct worker_s {
#define worker_count(w) 1
#define WORKER_SPAWN(b, i, c, o) do { c } while (0)
#endif /* WORKER_DISABLE */
#endif /* M_USE_WORKER */
#endif
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+6 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -25,9 +25,11 @@
#include "m-array.h"
#include "m-string.h"
#include "m-deque.h"
#include "test-obj.h"
#include "m-dict.h"
#include "m-algo.h"
#include "test-obj.h"
typedef struct over_s {
unsigned long data;
ILIST_INTERFACE(ilist_over, over_s);
@@ -42,6 +44,7 @@ ILIST_DEF(ilist_over, over_t, M_POD_OPLIST)
LIST_DEF(list_string, string_t, STRING_OPLIST)
DEQUE_DEF(deque_obj, testobj_t, TESTOBJ_CMP_OPLIST)
#define M_OPL_deque_obj_t() DEQUE_OPLIST(deque_obj, TESTOBJ_CMP_OPLIST)
DICT_DEF2(dict_obj, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
#include "coverage.h"
START_COVERAGE
@@ -50,6 +53,7 @@ ALGO_DEF(algo_list, LIST_OPLIST(list_int))
ALGO_DEF(algo_over, ILIST_OPLIST(ilist_over, M_POD_OPLIST))
ALGO_DEF(algo_string, LIST_OPLIST(list_string, STRING_OPLIST))
ALGO_DEF(algo_deque, deque_obj_t)
ALGO_DEF(algo_dict, DICT_OPLIST(dict_obj, STRING_OPLIST, TESTOBJ_OPLIST))
END_COVERAGE
int g_min, g_max, g_count;
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+72 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -407,6 +407,76 @@ static void test_queue(size_t n, int cpu_count, unsigned long long ref)
queue_uint_clear(g_buff2);
}
/********************************************************************************************/
static void test_spsc(void)
{
squeue_uint_t q;
bool b;
unsigned j;
squeue_uint_init(q, 256);
assert(squeue_uint_empty_p(q));
for(unsigned i = 0; i < 256; i++) {
b = squeue_uint_push(q, i);
assert(b);
}
assert(!squeue_uint_empty_p(q));
assert(squeue_uint_full_p(q));
assert(squeue_uint_size(q) == 256);
assert(squeue_uint_capacity(q) == 256);
b = squeue_uint_push(q, 256);
assert (!b);
for(unsigned i = 0; i < 256; i++) {
b = squeue_uint_pop(&j, q);
assert(b);
assert(j == i);
}
assert(squeue_uint_empty_p(q));
assert(!squeue_uint_full_p(q));
assert(squeue_uint_size(q) == 0);
assert(squeue_uint_capacity(q) == 256);
for(unsigned i = 0; i < 256; i++) {
b = squeue_uint_push(q, i);
assert(b);
}
for(unsigned i = 256; i < 256+128; i++) {
assert(squeue_uint_full_p(q));
squeue_uint_push_force(q, i);
}
for(unsigned i = 128; i < 256+128; i++) {
b = squeue_uint_pop(&j, q);
assert(b);
assert(j == i);
}
unsigned tab[16];
for(unsigned i = 0; i < 16; i++)
tab[i] = i * i;
for(unsigned i = 0; i < 15; i++) {
j = squeue_uint_push_bulk(q, 16, tab);
assert(j == 16);
}
b = squeue_uint_push(q, 1024);
assert(b);
j = squeue_uint_push_bulk(q, 16, tab);
assert(j == 15);
for(unsigned i = 0; i < 16; i++)
tab[i] = 0;
j = squeue_uint_pop_bulk(16, tab, q);
assert(j == 16);
for(unsigned i = 0; i < 16; i++)
assert( tab[i] == i * i);
squeue_uint_clear(q);
}
/********************************************************************************************/
int main(void)
@@ -438,5 +508,6 @@ int main(void)
test_no_thread();
test_global_ishared();
test_queue(1000000, 2, 2148371710223136ULL);
test_spsc();
exit(0);
}
+208
View File
@@ -0,0 +1,208 @@
/*
* M*LIB - Test for Concurrent memory pool allocator
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "m-c-mempool.h"
#include "m-mutex.h"
C_MEMPOOL_DEF(lf_mempool, int)
m_gc_t gc;
/**********************************************************************/
#define MAX_VALUE 100
#define MAX_THREAD 4
lf_mempool_t g;
typedef struct {
int step;
int *ptr[MAX_VALUE];
int value[MAX_VALUE];
} p_t;
p_t g_tab[MAX_THREAD];
static void do_stuff(m_gc_tid_t id)
{
// Mix of local and more global allocation
int *d[MAX_VALUE];
for(int i = 0; i < MAX_VALUE; i++) {
d[i] = lf_mempool_new(g, id);
*d[i] = i*i;
}
switch (g_tab[id].step)
{
case 0:
for(int i = 0; i < 50; i++) {
g_tab[id].value[i] += i;
g_tab[id].ptr[i] = lf_mempool_new(g, id);
*g_tab[id].ptr[i] = g_tab[id].value[i];
}
g_tab[id].step = 1;
break;
case 1:
for(int i = 50; i < MAX_VALUE; i++) {
g_tab[id].value[i] += i;
g_tab[id].ptr[i] = lf_mempool_new(g, id);
*g_tab[id].ptr[i] = g_tab[id].value[i];
}
g_tab[id].step = 2;
break;
case 2:
for(int i = 0; i < 50; i++) {
assert(g_tab[id].ptr[i] != NULL);
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
lf_mempool_del(g, g_tab[id].ptr[i], id);
g_tab[id].ptr[i] = NULL;
}
g_tab[id].step = 3;
break;
case 3:
for(int i = 50; i < MAX_VALUE; i++) {
assert(g_tab[id].ptr[i] != NULL);
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
lf_mempool_del(g, g_tab[id].ptr[i], id);
g_tab[id].ptr[i] = NULL;
}
g_tab[id].step = 0;
break;
default:
break;
}
for(int i = 0; i < MAX_VALUE; i++) {
assert (*d[i] == i*i);
lf_mempool_del(g, d[i], id);
}
}
static void thread1(void *arg)
{
(void)arg;
/* Attach this thread to the GC */
m_gc_tid_t id = m_gc_attach_thread(gc);
for(int n = 0; n < 100000; n ++) {
/* Awake the thread: now we can alloc nodes */
m_gc_awake(gc, id);
/* Do some stuff */
do_stuff(id);
/* Sleep the thread. A Garbage Collect may occur if possible */
m_gc_sleep(gc, id);
}
/* Detach the thread from the GC */
m_gc_detach_thread(gc, id);
}
static void thread2(void *arg)
{
(void)arg;
m_gc_tid_t id = m_gc_attach_thread(gc);
for(int n = 0; n < 10000; n ++) {
m_gc_awake(gc, id);
do_stuff(id);
m_thread_sleep(1);
m_gc_sleep(gc, id);
}
m_gc_detach_thread(gc, id);
}
static void test(void)
{
/* First create the Garbage Collector */
m_gc_init (gc, MAX_THREAD);
/* Create a mempool and attach it to the Garbage Collector */
lf_mempool_init(g, gc, 256, MAX_THREAD);
m_thread_t id[MAX_THREAD];
for(int i = 0; i < MAX_THREAD; i+=2) {
m_thread_create(id[i], thread1, NULL);
m_thread_create(id[i+1], thread2, NULL);
}
for(int i = 0; i < MAX_THREAD; i++) {
m_thread_join(id[i]);
}
/* Destroy the mempool */
lf_mempool_clear(g);
/* Destroy the Garbage Collector */
m_gc_clear (gc);
}
static void test2(void)
{
/* First create the Garbage Collector */
m_gc_init (gc, MAX_THREAD);
/* Create a mempool and attach it to the Garbage Collector */
lf_mempool_init(g, gc, 256, MAX_THREAD);
/* Create another mempool and attach it to the Garbage Collector */
lf_mempool_t g2;
lf_mempool_init(g2, gc, 256, MAX_THREAD);
/* Register VLA */
m_vlapool_t vla;
m_vlapool_init(vla, gc);
/* Attach the thread */
m_gc_tid_t id = m_gc_attach_thread(gc);
/* Awake the thread: now we can alloc nodes */
m_gc_awake(gc, id);
/* Test some allocation */
void *ptr = m_vlapool_new(vla, id, sizeof (int));
*(int*)ptr = 42;
m_vlapool_del(vla, ptr, id);
int *pi = lf_mempool_new(g, id);
*pi = 44;
lf_mempool_del(g, pi, id);
int *pi2 = lf_mempool_new(g2, id);
*pi2 = 46;
lf_mempool_del(g2, pi2, id);
/* Test that the data remain accessible
as it has only be logically deleted */
assert (*(int*)ptr == 42);
assert (*pi == 44);
assert (*pi2 == 46);
/* Sleep the thread */
m_gc_sleep(gc, id);
/* Detach the thread */
m_gc_detach_thread(gc, id);
/* Clear everything */
m_vlapool_clear(vla);
lf_mempool_clear(g);
lf_mempool_clear(g2);
m_gc_clear(gc);
}
int main(void)
{
test();
test2();
exit(0);
}
+47 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -82,8 +82,23 @@ ARRAY_DEF(key_list, string_t)
#define M_OPL_key_list_t() ARRAY_OPLIST(key_list, STRING_OPLIST)
CONCURRENT_DEF(key_list_ts, key_list_t)
CONCURRENT_RP_DEF(rparray1, array1_t, ARRAY_OPLIST(array1))
CONCURRENT_RP_DEF(rpdict1, dict1_t, DICT_OPLIST(dict1))
static inline bool int_oor_equal_p(int s, unsigned char n)
{
return s == -n;
}
static inline void int_oor_set(int *s, unsigned char n)
{
*s = -n;
}
#define INT_OA_OPLIST \
M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(int_oor_equal_p), OOR_SET(API_2(int_oor_set)))
DICT_OA_DEF2(dict3, int, INT_OA_OPLIST, int, M_DEFAULT_OPLIST)
CONCURRENT_DEF(pdict3, dict3_t, DICT_OPLIST(dict3))
/********************************/
parray1_t arr;
@@ -98,7 +113,7 @@ static void conso(void *p)
}
}
static void test(void)
static void test_thread(void)
{
m_thread_t idx;
parray1_init(arr);
@@ -139,6 +154,34 @@ static void test_basic(void)
}
}
rparray1_t rarr;
static void conso_rp(void *p)
{
assert (p == NULL);
for(int i = 0; i < 1000; i++) {
int j;
bool b = rparray1_pop_blocking(&j, rarr, true);
assert (b);
assert (j == i);
}
}
static void test_rp_thread(void)
{
m_thread_t idx;
rparray1_init(rarr);
m_thread_create (idx, conso_rp, NULL);
m_thread_sleep(1000);
for(int i = 0; i < 1000; i++) {
rparray1_push (rarr, i);
while (!rparray1_empty_p(rarr));
}
m_thread_join(idx);
rparray1_clear(rarr);
}
static void test_rp_basic(void)
{
rpdict1_t dict;
@@ -159,6 +202,7 @@ int main(void)
{
test_basic();
test_rp_basic();
test();
test_thread();
test_rp_thread();
exit(0);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+49 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -41,6 +41,11 @@ DICT_SET_DEF(dict_setstr, string_t, STRING_OPLIST)
DICT_DEF2(dict_int, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
DICT_DEF2(dict_mpz, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
BOUNDED_STRING_DEF(symbol, 15)
DICT_OA_DEF2(dict_oa_str, symbol_t, BOUNDED_STRING_OPLIST(symbol), int, M_DEFAULT_OPLIST)
DICT_OA_DEF2(dict_oa_bstr, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST)
/* Helper structure */
ARRAY_DEF(array_string, string_t, STRING_OPLIST)
array_string_t v_str;
@@ -463,6 +468,47 @@ static void test_it_oa(void)
}
}
static void
test_oa_str1(void)
{
symbol_t s;
int *p;
dict_oa_str_t dict;
symbol_init(s);
dict_oa_str_init(dict);
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "x"), 1);
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "y"), 2);
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "z"), 3);
p = dict_oa_str_get_at(dict, BOUNDED_STRING_CTE(symbol, "x"));
assert(p != NULL);
assert(*p == 1);
dict_oa_str_clear(dict);
symbol_clear(s);
}
static void
test_oa_str2(void)
{
int *p;
dict_oa_bstr_t dict;
dict_oa_bstr_init(dict);
dict_oa_bstr_set_at(dict, STRING_CTE("x"), 1);
dict_oa_bstr_set_at(dict, STRING_CTE("y"), 2);
dict_oa_bstr_set_at(dict, STRING_CTE("z"), 3);
p = dict_oa_bstr_get_at(dict, STRING_CTE("x"));
assert(p != NULL);
assert(*p == 1);
dict_oa_bstr_clear(dict);
}
int main(void)
{
test1();
@@ -472,5 +518,7 @@ int main(void)
test_oa();
test_init_oa();
test_it_oa();
test_oa_str1();
test_oa_str2();
exit(0);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+47 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -91,12 +91,17 @@ static void test_utf8_basic(void)
static void test_utf8_it(void)
{
string_t s;
string_it_t it;
string_init(s);
for(string_it(it, s) ; !string_end_p(it); string_next(it)) {
assert(0); // Shall not be called
}
const unsigned int tab[] = { 45, 1458, 25623, 129, 24 };
for(int i = 0; i < 5; i++)
string_push_u (s, tab[i]);
assert (string_length_u(s) == 5);
string_it_t it;
int i = 0;
for(string_it(it, s) ; !string_end_p(it); string_next(it), i++) {
assert (i < 5);
@@ -384,6 +389,9 @@ static void test0(void)
string_init (s1);
string_init (s2);
size_t h1 = string_hash(s1);
assert (h1 != 0);
string_printf (s1, "Hello %d worlds. How do you do? I'm fine. Thank you! The weather is bad today. I should had brought my umbrella. Oh! You can lend me one! Thank you very much! No really thank you. I wouldn't be able to get in time for my job.", 2);
assert (string_equal_str_p(s1, "Hello 2 worlds. How do you do? I'm fine. Thank you! The weather is bad today. I should had brought my umbrella. Oh! You can lend me one! Thank you very much! No really thank you. I wouldn't be able to get in time for my job."));
@@ -447,7 +455,7 @@ static void test0(void)
string_set_str (s1, "");
assert (string_capacity(s1) > 0);
string_reserve (s1, 0);
assert (string_capacity(s1) == 0);
assert (string_capacity(s1) == sizeof (string_heap_t) - 1);
string_set_str(s1, "HELLO XXX!");
string_replace_at(s1, 6, 3, "World");
@@ -518,6 +526,41 @@ static void test_bounded1(void)
string16_clear(s);
}
static void test_bounded_io(void)
{
string16_t s, d;
bool b;
FILE *f;
string16_init(s);
string16_init(d);
string16_set_str(s, "Hello \"world\"");
f = fopen ("a-mstring.dat", "wt");
assert (f != NULL);
string16_out_str (f, s);
fclose (f);
f = fopen("a-mstring.dat", "rt");
assert (f != NULL);
b = string16_in_str(d, f);
assert(b);
fclose(f);
assert (string16_equal_p(s, d));
const char *end;
string_t str;
string_init(str);
string16_get_str(str, s, false);
assert(string_equal_str_p(str, "\"Hello \\\"world\\\"\""));
b = string16_parse_str(d, string_get_cstr(str), &end);
assert (b);
assert (string16_equal_p(s, d));
string_clear(str);
string16_clear(s);
string16_clear(d);
}
int main(void)
{
test0();
@@ -525,5 +568,6 @@ int main(void)
test_utf8_basic();
test_utf8_it();
test_bounded1();
test_bounded_io();
exit(0);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -76,7 +76,7 @@ static void test1bis(void)
assert (atomic_load(&resetFunc_called) == true || workeri_get_cpu_count() == 1);
}
#if defined(__GNUC__) && (!defined(__clang__) || WORKER_USE_CLANG_BLOCK || WORKER_USE_CPP_FUNCTION)
#if defined(__GNUC__) && (!defined(__clang__) || (defined(WORKER_USE_CLANG_BLOCK) && WORKER_USE_CLANG_BLOCK) || (defined(WORKER_USE_CPP_FUNCTION) && WORKER_USE_CPP_FUNCTION))
/* The macro version will generate warnings about shadow variables.
There is no way to avoid this. */
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: