Install (developer documentation) file for the sm_producer_consumer (SM Producer Consumer) tool. ---------------------------------------- In this document Developer can find necessary info about structure of this solution (SM Producer Consumer), necessary tools and how this solution can be built and tested. Downloading the SM Producer Consumer source files: -------------------- Source files of the sm_producer_consumer (SM Producer Consumer) solution can be found here: http://smansoft.com/sm_producer_consumer/ Also, developer can find source files here: https://github.com/smansoft/sm_producer_consumer . Developer can use command git clone https://github.com/smansoft/sm_producer_consumer.git to get sources from github repository. The basic directory layout of SM Producer Consumer solution is as follows: -------------------- / - root directory of sources (but not root of the OS); _bin/ bin/ sm_producer_consumer - main binary file (copied from the / directory during execution 'make install' command); log/ sm_producer_consumer.log - log output file, that has been generated by sm_producer_consumer (main binary file) during execution; share/ smansoft/ doc/ - these files have been copied from the / directory during execution 'make install' command; README - user manual of the application; HELP - short reference; INSTALL - developer manual of the project (this document); LICENSE - License file of the project and application (MIT License); CHANGELOG - the changelog file for the sm_calculator (SM Calculator) tool; sm_producer_consumer/ - application Data directory; sm_producer_consumer.out.txt - result file, that is generated by sm_producer_consumer (main binary file) during execution: contains lines: len: 30 line: 165419817718862070872477220052 len: 20 line: 39200062562017294509 .stop_ - temporary file; .stop - stop file, if this file is found in this directory, sm_producer_consumer (main binary file) exits; just rename .stop_ for finish execution sm_producer_consumer (main binary file); sstring_lib-0.0.1.1/ - secure string and safe memory control library (instead C run-time functions); include/ - headers; safe_clib_private.h safe_gets.h safe_lib.h safe_mem_lib.h safe_snprintf.h safe_str_lib.h safe_fopen.h safe_lib_errno.h safe_mem_constraint.h safe_mem_primitives_lib.h safe_str_constraint.h safe_types.h src/ - sources; safe_abort_handler.c safe_memmove16.c safe_stpcpy.c safe_strcpyfldout.c safe_strispassword.c safe_strprefix.c safe_wcsncat.c safe_fopen.c safe_memmove32.c safe_stpncpy.c safe_strcspn.c safe_strisuppercase.c safe_strremovews.c safe_wcsncpy.c safe_gets.c safe_memmove.c safe_strcasecmp.c safe_strfirstchar.c safe_strlastchar.c safe_strspn.c safe_wcsnlen.c safe_ignore_handler.c safe_mem_primitives_lib.c safe_strcasestr.c safe_strfirstdiff.c safe_strlastdiff.c safe_strstr.c safe_wmemcmp.c safe_memcmp16.c safe_memset16.c safe_strcat.c safe_strfirstsame.c safe_strlastsame.c safe_strtok.c safe_wmemcpy.c safe_memcmp32.c safe_memset32.c safe_strcmp.c safe_strisalphanumeric.c safe_strljustify.c safe_strtolowercase.c safe_wmemmove.c safe_memcmp.c safe_memset.c safe_strcmpfld.c safe_strisascii.c safe_strncat.c safe_strtouppercase.c safe_wmemset.c safe_mem_constraint.c safe_memzero16.c safe_str_constraint.c safe_strisdigit.c safe_strncpy.c safe_strzero.c safe_memcpy16.c safe_memzero32.c safe_strcpy.c safe_strishex.c safe_strnlen.c safe_wcpcpy.c safe_memcpy32.c safe_memzero.c safe_strcpyfld.c safe_strislowercase.c safe_strnterminate.c safe_wcscat.c safe_memcpy.c safe_snprintf_support.c safe_strcpyfldin.c safe_strismixedcase.c safe_strpbrk.c safe_wcscpy.c obj/ - object binary files, have been created during execution 'make'; Makefile - make file (for Make tool), contains instructions for building the project; libsstring_lib-0.0.1.1.a - binary module of the library, have been created after execution 'make'; sm_log_lib-0.0.2.2/ - simple, compact logging library (supports synchronizing, log-level output, output to the file and/or to the console); include/ - headers; framework.h pch.h sm_files_tools.h sm_log.h sm_log_types.h src/ - sources; sm_files_tools.c sm_log.c obj/ - object binary files, have been created during execution 'make'; Makefile - make file (for Make tool), contains instructions for building the project; libsm_log_lib-0.0.2.2.a - binary module of the library, have been created after execution 'make'; include/ - headers of the sm_producer_consumer (SM Producer Consumer) solution;; pch.h sm_consumer_fin.h sm_consumer_mid.h sm_log_tool.h sm_producer_factory.h sm_producer_item.h sm_consumer_factory.h sm_consumer_item.h sm_exceptions.h sm_main.h sm_producer.h sm_tools.h src/ - sources of the sm_producer_consumer (SM Producer Consumer) solution; sm_consumer_factory.cpp sm_consumer_item.cpp sm_exceptions.cpp sm_main.cpp sm_producer_factory.cpp sm_tools.cpp sm_consumer_fin.cpp sm_consumer_mid.cpp sm_log_tool.cpp sm_producer.cpp sm_producer_item.cpp obj/ - object binary files, have been created during execution 'make'; _clean.sh - bash script, that just run 'make clean'; _build.release.sh - bash script, that run Make tool and gets Release build of the solution; _build.debug.sh - bash script, that run Make tool and gets Debug build of the solution; Makefile - make file (for Make tool), contains instructions for building the project and dependencies (sstring_lib-0.0.1.1 and sm_log_lib-0.0.2.2); README - user manual of the application; HELP - short reference; INSTALL - developer manual of the project (this document); LICENSE - License file of the project and application (MIT License); CHANGELOG - the changelog file for the sm_calculator (SM Calculator) tool; sm_producer_consumer - main binary file (has been created during execution 'make'); Projects of the solution : -------------------- sm_producer_consumer (/) - main project of the solution; sm_log_lib-0.0.2.2 (sm_log_lib-0.0.2.2/) - small logging library (with synchronizing support); this library is built in this solution as static module/library; sstring_lib-0.0.1.1 (sstring_lib-0.0.1.1/) - secure string and safe memory control library (instead C run-time functions); this project is based on safestringlib; original sources can be found here: http://smansoft.com/sm_calculator/0.2.0.4/safestringlib/ http://smansoft.com/sm_calculator/0.2.0.4/safestringlib/safestringlib.zip http://smansoft.com/sm_calculator/0.2.0.4/safestringlib/safestringlib.tar.gz also developer can find these sources here: https://github.com/intel/safestringlib/ git clone https://github.com/intel/safestringlib.git original safestringlib project is published under MIT License; sstring_lib basically contains functionality imported from original safestringlib, some small changes have been provided and several functions have been added; this library is built in this solution as static module/library; Tools necessary for build of the project: -------------------- At the current step, this solution has been built and tested only at the Linux platform; - Linux platform: - GCC; - Make tool; - bash; The solution assembly has been tested at follow Linux distributions: Debian 10 (x64/x32); Debian 9 (x64/x32); openSUSE 15.1 (x64); openSUSE 15.2 (x64); Ubuntu 20.04 LTS (x64); Mint 19.3 (x64); Mint 20.1 (x64); Assembly of the solution: -------------------- -------------------- Linux platform: -------------------- - just run: make clean make (without parameters builds Debug version) or make SM_FILE_BUILD=Debug; (builds Debug version) or make SM_FILE_BUILD=Release; (builds Release version) make install or launch follow scripts: _clean.sh - Clean up all binaries _build.debug.sh - Debug build _build.release.sh - Release build - after execution: make install - main binary file will be copied to the _bin/bin/ directory; - documentation files: README HELP INSTALL LICENSE CHANGELOG will be copied to the main binary file will be copied to the _bin/share/smansoft/doc/ directory; Main solution classes: -------------------- /** * CSMProducerFactory * * multi-thread synchronized singleton; * * storage and control CSMProducerBase based (inherited) objects (several instances), * i.e. instances of * follow classes CSMProducerType1, CSMProducerType2, * CSMProducerType3, CSMProducerType4, CSMProducerType5 */ class CSMProducerFactory; std::vector> m_producers; // vector of pointers to CSMProducerBase // (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) objects std::mutex m_mtx_producers; // synchronization primitive, that makes // m_mtx_producers an thread-safe queue static CSMProducerFactory* m_instance; // pointer to instance of the singleton static std::mutex m_mtx_instance; // thread-safe pointer /** * CSMConsumerFactory * * multi-thread synchronized singleton; * * storage and control CSMConsumerMid objects (several, created for every Producer) * and CSMConsumerFin object (single object); */ class CSMConsumerFactory; std::vector> m_consumer_mids; // vector of pointers to CSMConsumerMid objects std::mutex m_mtx_consumer_mids; // synchronization primitive, that makes // m_consumer_mids an thread-safe queue std::shared_ptr m_consumer_fin; // pointer to the instance of CSMConsumerFin static CSMConsumerFactory* m_instance; // pointer to instance of the singleton static std::mutex m_mtx_instance; // thread-safe pointer /** * CSMProducerItem * * data class, that just contains array of unsigned chars * (std::shared_ptr m_data) * and length of this array (m_size); * * class provides interface (api), that allows to get and change these data * (array of unsigned chars and length of this array); */ class CSMProducerItem; std::shared_ptr m_data; // data (array of unsigned chars), created by one of Producer classes // CSMProducerType1, CSMProducerType2, ... CSMProducerType5 unsigned int m_size; // length of m_data array (including last zero symbol) /** * CSMConsumerItem * * data class, that just contains array of unsigned chars * (std::shared_ptr m_data) * and length of this array (m_size); * * class provides interface (api), that allows to get and change these data * (array of unsigned chars and length of this array); */ class CSMConsumerItem; std::shared_ptr m_data; // data (array of unsigned chars), created by Consumer class // during converting (copying) from CSMProducerItem unsigned int m_size; // length of m_data array (including last zero symbol) /** * CSMProducerBase * * base class of suite of Producers, which provide generation * of Item data instances of the CSMProducerItem class, * using separate thread; * * generated items are inserted into queue: * std::queue m_items_queue; * * m_items_queue has limited size (defined by m_items_queue_max); * * extracts generated items from the queue: * std::queue m_items_queue; * * uses synchronizing primitives for access to the queue (m_items_queue): * std::mutex m_mtx_queue; * std::condition_variable m_cv_queue; * * periodicity of generation of items are defined by m_delay (sleep value) * * producer(some ProducerType type) --> m_items_queue */ class CSMProducerBase; unsigned int m_max_num; // max number of integers in random line, // generated during one step unsigned int m_delay; // delay in msecs (sleep time in the thread) unsigned int m_items_queue_max; // max size of m_items_queue (queue of CSMProducerItem elements) void genProducerItem(); // generation the CSMProducerItem and inserting it into // std::queue m_items_queue // is called by threadFunc (by the thread function) void threadFunc(); // thread function (is used by std::unique_ptrm_thread) const ProducerType m_type; // type of producer // same as in CSMProducerType1, CSMProducerType2, ... CSMProducerType5 // Producers and consumers, which have same type are joined (are used same // std::queue m_items_queue) std::queue m_items_queue; // queue, that is generated in the thread, created by the producer std::mutex m_mtx_queue; // synchronization primitive (mutex) for access to m_items_queue std::condition_variable m_cv_queue; // synchronization primitive (mutex) for access to m_items_queue // condition is used, when m_mtx_queue is locked, but // m_items_queue is empty or its size equals m_items_queue_max // i.e. maximum value and new items can't be added // and producer should wait till CSMConsumerFin extracts // item from m_items_queue std::unique_ptrm_thread; // thread, that is used for generation of CSMProducerItem in m_items_queue bool m_run_thread; // if m_run_thread == false, thread (m_thread) will be finished std::mt19937 m_rnd_eng; // default constructed, seeded with fixed seed void genProducerItem(); // generation the CSMProducerItem and inserting it into // std::queue m_items_queue // is called by threadFunc (by the thread function) void threadFunc(); // thread function (is used by std::unique_ptrm_thread) /** * CSMProducerType1 * * type of Producer 1 * * uses: * * m_max_num = DEF_MAX_SIZE_RND_TYPE1; * m_delay = DEF_PRODUCER_DELAY_TYPE1; * m_items_queue_max = DEF_PRODUCER_QUEUE_TYPE1_MAX; * * DEF_MAX_SIZE_RND_TYPE1 8 * DEF_PRODUCER_DELAY_TYPE1 111 * DEF_PRODUCER_QUEUE_TYPE1_MAX 100 */ class CSMProducerType1 : public CSMProducerBase; /** * CSMProducerType2 * * type of Producer 2 * * uses: * * m_max_num = DEF_PRODUCER_DELAY_TYPE2; * m_delay = DEF_PRODUCER_DELAY_TYPE2; * m_items_queue_max = DEF_PRODUCER_QUEUE_TYPE2_MAX; * * DEF_MAX_SIZE_RND_TYPE2 16 * DEF_PRODUCER_DELAY_TYPE2 222 * DEF_PRODUCER_QUEUE_TYPE2_MAX 100 */ class CSMProducerType2 : public CSMProducerBase; /** * CSMProducerType3 * * type of Producer 3 * * uses: * * m_max_num = DEF_PRODUCER_DELAY_TYPE3; * m_delay = DEF_PRODUCER_DELAY_TYPE3; * m_items_queue_max = DEF_PRODUCER_QUEUE_TYPE3_MAX; * * DEF_MAX_SIZE_RND_TYPE3 32 * DEF_PRODUCER_DELAY_TYPE3 333 * DEF_PRODUCER_QUEUE_TYPE3_MAX 100 */ class CSMProducerType3 : public CSMProducerBase; /** * CSMProducerType4 * * type of Producer 4 * * uses: * * m_max_num = DEF_PRODUCER_DELAY_TYPE4; * m_delay = DEF_PRODUCER_DELAY_TYPE4; * m_items_queue_max = DEF_PRODUCER_QUEUE_TYPE4_MAX; * * DEF_MAX_SIZE_RND_TYPE4 64 * DEF_PRODUCER_DELAY_TYPE4 555 * DEF_PRODUCER_QUEUE_TYPE4_MAX 100 */ class CSMProducerType4 : public CSMProducerBase; /** * CSMProducerType5 * * type of Producer 5 * * uses: * * m_max_num = DEF_PRODUCER_DELAY_TYPE5; * m_delay = DEF_PRODUCER_DELAY_TYPE5; * m_items_queue_max = DEF_PRODUCER_QUEUE_TYPE5_MAX; * * DEF_MAX_SIZE_RND_TYPE5 128 * DEF_PRODUCER_DELAY_TYPE5 777 * DEF_PRODUCER_QUEUE_TYPE5_MAX 100 */ class CSMProducerType5 : public CSMProducerBase; /** * CSMConsumerMid * * intermediate consumer class, that has some type * (like CSMProducerType1, CSMProducerType2,... CSMProducerType5) * * instance of this class (CSMConsumerMid) is joined to * the instance of producer class * (CSMProducerType1, CSMProducerType2, ... CSMProducerType5, ProducerType producer_type); * * this class creates thread, that reads data queue, * m_items_queue (CSMProducerItem) * that filled by the correspondent producer; * * this class extracts CSMProducerItem from m_items_queue, * converts CSMProducerItem -> CSMConsumerItem * and inserts CSMConsumerItem to * std::queue m_items_queue * (one instance in the CSMConsumerFin class); */ class CSMConsumerMid; void processProducerConsumerItem(); // is called by the threadFunc function // provides processing of data: // reading from (via CSMProducerBase *m_producer (one of CSMProducerType1, CSMProducerType2, ... CSMProducerType5)), // generating CSMConsumerItem and inserting it to common/shared (for all Consumer threads) queue // std::queue m_items_queue // in the CSMConsumerFin class void threadFunc(); // thread function (is used by std::unique_ptrm_thread) ProducerType m_producer_type; // type of producer CSMProducerBase *m_producer; // pointer to joined producer (one of CSMProducerType1, CSMProducerType2, ... CSMProducerType5)) // initialized in the constructor and depends on (ProducerType m_producer_type) std::unique_ptrm_thread; // thread, that is used for reading the queue from // std::queue m_items_queue // (via CSMProducerBase *m_producer (one of CSMProducerType1, CSMProducerType2, ... CSMProducerType5)), // generation CSMConsumerItem and inserting it to // std::queue m_items_queue // in the CSMConsumerFin class // instance of the CSMConsumerFin class is obtained via singleton: CSMConsumerFactory bool m_run_thread; // if m_run_thread == false, thread (m_thread) will be finished unsigned int m_delay; // delay in msecs (sleep time in the thread) /** * CSMConsumerFin * * final consumer class,contains queue of CSMConsumerItem elements: * std::queue m_items_queue; * * m_items_queue has limited size (defined by m_items_queue_max); * * opens text file (m_consumer_output) (file path: m_consumer_output_fpath); * * creates thread, that reads queue of CSMConsumerItem elements: * std::queue m_items_queue; * and writes content (unsigned char array and length) into * the output text file (m_consumer_output) in the follow format; * * len: 30 line: 165419817718862070872477220052 * len: 20 line: 39200062562017294509 * len: 50 line: 17602029623591885893229577471612569214254259623284 * len: 106 line: 2312287005485858224349874085119518972282993005321207792955811589305733100220415361105722182633373291193122 * len: 57 line: 276877865641402651882762481943439227966147003631211487406 * len: 30 line: 336727954911150551753810078128 * len: 97 line: 4493950942307061909176887685717840987692401872029587605656171416438224493589713863559573367050933 * * uses synchronizing primitives for access to the queue (m_items_queue): * std::mutex m_mtx_queue; * std::condition_variable m_cv_queue; * * periodicity of generation of items are defined, by m_delay (sleep value) */ class CSMConsumerFin; void processConsumerItem(); // is called by the threadFunc() function // - reading data (CSMConsumerItem) from std::queue m_items_queue // - writing data to the file std::ofstream m_consumer_output void threadFunc(); // thread function (is used by std::unique_ptrm_thread) std::unique_ptrm_thread; // thread of this consumer class, provides processing of data: threadFunc() -> readData() bool m_run_thread; // if m_run_thread == false, thread (m_thread) will be finished unsigned int m_items_queue_max; // max size of m_items_queue (queue of CSMConsumerItem elements) std::queue m_items_queue; // queue of CSMConsumerItem elements, which are generated in threads, created by CSMConsumerMid objects std::mutex m_mtx_queue; // synchronization primitive (mutex) for access to m_items_queue std::condition_variable m_cv_queue; // synchronization primitive (mutex) for access to m_items_queue // condition is used, when m_mtx_queue is locked, but // m_items_queue is empty or its size equals m_items_queue_max // i.e. maximum value and new items can't be added // and CSMConsumerMid, calling addConsumerItem, should wait till the readData function extracts // item from m_items_queue std::string m_consumer_output_fpath; // full file path of m_consumer_output std::ofstream m_consumer_output; // file, where result data (lines) will be saved: // len: 30 line: 165419817718862070872477220052 // len: 20 line: 39200062562017294509 unsigned int m_delay; // delay in msecs (sleep time in the thread) Main workflow of the solution: -------------------- Main workflow of the sm_producer_consumer (SM Producer Consumer) tool separated to 4 parts. Parts 1,2,3 can be pasted together, as the part 2 is a continue of the part 1 and the part 3 is a continue of the part 2: 1. CSMProducerType1 --(adding in the thread 1)--> queue 1 <--(extracting in the thread 6 )-- CSMConsumerMid (instance 1) CSMProducerType2 --(adding in the thread 2)--> queue 2 <--(extracting in the thread 7 )-- CSMConsumerMid (instance 2) CSMProducerType3 --(adding in the thread 3)--> queue 3 <--(extracting in the thread 8 )-- CSMConsumerMid (instance 3) CSMProducerType4 --(adding in the thread 4)--> queue 4 <--(extracting in the thread 9 )-- CSMConsumerMid (instance 4) CSMProducerType5 --(adding in the thread 5)--> queue 5 <--(extracting in the thread 10)-- CSMConsumerMid (instance 5) 2. CSMConsumerMid (instance 1) --(adding in the thread 6 )--> | | CSMConsumerMid (instance 2) --(adding in the thread 7 )--> | common queue | CSMConsumerMid (instance 3) --(adding in the thread 8 )--> | in the class | CSMConsumerMid (instance 4) --(adding in the thread 9 )--> | CSMConsumerFin | CSMConsumerMid (instance 5) --(adding in the thread 10)--> | | 3. | | | common queue | | in the class | <--(extracting in the thread 11)-- CSMConsumerFin --(adding in the thread 11 )--> output file ("sm_producer_consumer.out.txt") | CSMConsumerFin | | | 4. main() --(main thread, created by OS, checking file)--> file .stop Main workflow of the solution (description): -------------------- 1. After launch, main() creates 5 producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) and 5 instances of CSMConsumerMid (various types CSMProducerType1, CSMProducerType2, ... CSMProducerType5); 2. Also main() creates 1 instance of the CSMConsumerFin object; 3. Producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) and correspondent CSMConsumerMid (various types CSMProducerType1, CSMProducerType2, ... CSMProducerType5) are joined together for processing during creating and initializing, by factory classes-singletons CSMProducerFactory, CSMConsumerFactory; 4. Every instance of producer (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) has its own queue: std::queue m_items_queue and defined max limit of this queue: unsigned int m_items_queue_max; 5. Every producer creates it's own thread, that generates data object (CSMProducerItem) and inserts him into own queue: std::queue m_items_queue; 6. Every CSMProducerItem contains random-generated random-size unsigned char array (set of random-generated integers) and length of generated unsigned char array (including last zero symbol); 7. Periodicity/frequency of CSMProducerItem generation is defined by unsigned int m_delay value (sleep of the thread); Periodicity/frequency of various producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) is different; 8. Every instance of consumer CSMConsumerMid (various types CSMProducerType1, CSMProducerType2, ... CSMProducerType5) also creates own thread and provides reading/extracting of queue (in concurrency mode) from correspondent producer (CSMProducerType1, CSMProducerType2, ... CSMProducerType5); 9. Also the consumer (in the same thread) generates new data object (CSMConsumerItem), using extracted CSMProducerItem (from the queue) and inserts it into queue, that is a property of the CSMConsumerFin object: std::queue m_items_queue; also defined max limit of this queue: unsigned int m_items_queue_max; 10. Insert of CSMConsumerItem objects is provided in concurrency mode, as all CSMConsumerMid instances inserts into same queue, that is a property of one CSMConsumerFin object; 11. Periodicity/frequency of reading of queue by CSMConsumerMid is defined by unsigned int m_delay value; 12. CSMConsumerFin creates its own thread and provides reading/extracting data objects (CSMConsumerItem), reading data from them; 13. CSMConsumerFin in the same thread also writes read unsigned char arrays to result output file: sm_producer_consumer.out.txt in the directory _bin/share/smansoft/sm_producer_consumer/; I.e. as result, all content (unsigned char arrays) generated by producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) be written to the file sm_producer_consumer.out.txt. 14. Queues, which are properties of producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) have max limit property: unsigned int m_items_queue_max; 15. Queue, that is a property of the CSMConsumerFin object has max limit property: unsigned int m_items_queue_max; 16. As producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5), consumers (CSMConsumerMid) and final consumer CSMConsumerFin have various periodicity/frequency of generation/extracting/processing of items, there is some problem: coordination of their work, if queues are empty or overflowed (size of queue equals to defined max limit of these queues); 17. This problem (coordination of their work, if queues are empty or overflowed) has been resolved, using synchronizing primitives like: std::mutex m_mtx_queue; std::condition_variable m_cv_queue; 18. If during work, some queue (in producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) or in the final consumer CSMConsumerFin) is empty, in this case reading/extracting thread is suspended till writing/generating thread inserts new item; Then reading/extracting thread is resumed; 19. If during work, some queue (in producers (CSMProducerType1, CSMProducerType2, ... CSMProducerType5) or in the final consumer CSMConsumerFin) is overflowed (size of queue equals to defined max limit of these queues), writing/generating thread is suspended till reading/extracting thread reads/extracts item from the queue; Then writing/generating thread is resumed; 20. After launching multi-thread generation/extracting/processing main() enters in the cycle (with 3 seconds sleep/delay), where checks if the file .stop in the directory _bin/share/smansoft/sm_producer_consumer/ can be opened (for reading); If main() finds the file .stop in the directory _bin/share/smansoft/sm_producer_consumer/, this cycle will be finished; 21. After finishing the cycle (opening file .stop), main() finishes all threads, using flags (bool m_run_thread), prints to the log not-processed items from queue in the CSMConsumerFin object, deletes all singletons and exits; Results of the solution execution: -------------------- Results can be found: 1. Log file sm_producer_consumer.log in the directory _bin/log/; 2. Text file sm_producer_consumer.out.txt in the directory _bin/share/smansoft/sm_producer_consumer/; -------------------- Copyright (C) 2021 SManSoft Sergey Manoylo