You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							486 lines
						
					
					
						
							13 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							486 lines
						
					
					
						
							13 KiB
						
					
					
				| /* ///////////////////////////////////////////////////////////////////////// | |
|  * File:        test/component/test.component.be.file.threading/test.component.be.file.threading.cpp | |
|  * | |
|  * Purpose:     Implementation file for the test.component.be.file.threading project. | |
|  * | |
|  * Created:     3rd July 2009 | |
|  * Updated:     10th January 2011 | |
|  * | |
|  * Status:      Wizard-generated | |
|  * | |
|  * License:     (Licensed under the Synesis Software Open License) | |
|  * | |
|  *              Copyright (c) 2009-2011, Synesis Software Pty Ltd. | |
|  *              All rights reserved. | |
|  * | |
|  *              www:        http://www.synesis.com.au/software | |
|  * | |
|  * ////////////////////////////////////////////////////////////////////// */ | |
| 
 | |
| 
 | |
| #include <pantheios/util/test/compiler_warnings_suppression.first_include.h> | |
|  | |
| /* Pantheios Header Files */ | |
| #include <pantheios/pan.hpp> | |
| #include <pantheios/backends/be.N.h> | |
| #include <pantheios/backends/bec.console.h> | |
| #include <pantheios/backends/bec.file.h> | |
| #include <pantheios/frontends/fe.N.h> | |
| #include <pantheios/inserters/args.hpp> | |
| #include <pantheios/inserters/blob.hpp> | |
| #include <pantheios/inserters/exception.hpp> | |
| #include <pantheios/inserters/integer.hpp> | |
|  | |
| /* STLSoft Header Files */ | |
| #include <stlsoft/string/split_functions.hpp> | |
| #include <stlsoft/string/string_view.hpp> | |
| #include <platformstl/filesystem/file_lines.hpp> | |
| #include <platformstl/filesystem/filesystem_traits.hpp> | |
| #include <platformstl/synch/sleep_functions.h> | |
|  | |
| /* Standard C++ Header Files */ | |
| #include <exception> | |
|  | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|  | |
|  /* PThreads Header Files */ | |
| # include <pthread.h> | |
| # include <sched.h> | |
|  | |
|  /* UNIXEm Header Files */ | |
| # if defined(_WIN32) || \ | |
|      defined(_WIN64) | |
| #  include <unixem/unixem.h> | |
| # endif /* Win32 || Win64 */ | |
|  | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
|  | |
| # define WINSTL_ERROR_DESC_NO_IMPLICIT_CONVERSION | |
| # include <winstl/error/error_desc.hpp> | |
|  | |
| # include <windows.h> | |
|  | |
| #else /* ? OS */ | |
| # error Operating system not discriminated | |
| #endif /* OS */ | |
|  | |
| /* Standard C Header Files */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
|  | |
| #if defined(_MSC_VER) && \ | |
|     defined(_DEBUG) | |
| # define USE_MSC_VER_CRT_MEM_CHECKS | |
| # include <crtdbg.h> | |
| #endif /* _MSC_VER) && _DEBUG */ | |
|  | |
| #include <pantheios/util/test/compiler_warnings_suppression.last_include.h> | |
|  | |
| /* ////////////////////////////////////////////////////////////////////// */ | |
| 
 | |
| #define PSTR(x)         PANTHEIOS_LITERAL_STRING(x) | |
|  | |
| /* ///////////////////////////////////////////////////////////////////////// | |
|  * Character encoding | |
|  */ | |
| 
 | |
| #ifdef PANTHEIOS_USE_WIDE_STRINGS | |
|  | |
| # define XTESTS_TEST_STRING_EQUAL       XTESTS_TEST_WIDE_STRING_EQUAL | |
|  | |
| #else /* ? PANTHEIOS_USE_WIDE_STRINGS */ | |
|  | |
| # define XTESTS_TEST_STRING_EQUAL       XTESTS_TEST_MULTIBYTE_STRING_EQUAL | |
|  | |
| #endif /* PANTHEIOS_USE_WIDE_STRINGS */ | |
|  | |
| /* ///////////////////////////////////////////////////////////////////////// | |
|  * Constants and definitions | |
|  */ | |
| 
 | |
| #ifdef _DEBUG | |
| const size_t    LOG_ITERATIONS      =   10; | |
| const size_t    SET_PATH_DELAY      =   1000 * 100; | |
| #else /* ? _DEBUG */ | |
|  /* Surprisingly, Windows takes massively more time than UNIX to write | |
|   * out all the entries, so we need different ITERATIONS count otherwise | |
|   * Windows users running the component tests may think the process has | |
|   * hung and kill it | |
|   */ | |
| # if defined(PLATFORMSTL_OS_IS_WINDOWS) | |
| const size_t    LOG_ITERATIONS      =   5000; | |
| # else /* ? OS */ | |
| const size_t    LOG_ITERATIONS      =   100000; | |
| # endif /* OS */ | |
| const size_t    SET_PATH_DELAY      =   1000 * 1000 * 10; | |
| #endif /* _DEBUG */ | |
|  | |
| const PAN_CHAR_T LOG_FILE_NAME[]    =   PSTR("test.component.be.file.threading.log"); | |
| 
 | |
| /* ///////////////////////////////////////////////////////////////////////// | |
|  * Globals | |
|  */ | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
| static pthread_mutex_t  s_mx            =   PTHREAD_MUTEX_INITIALIZER; | |
| static pthread_cond_t   s_cv            =   PTHREAD_COND_INITIALIZER; | |
| static int              s_activeThreads =   0; | |
| #endif /* OS */ | |
|  | |
| static int              s_showNotices   =   true; | |
| 
 | |
| extern "C" const PAN_CHAR_T PANTHEIOS_FE_PROCESS_IDENTITY[]    =   PSTR("test.component.be.file.threading"); | |
| 
 | |
| /* ///////////////////////////////////////////////////////////////////////// | |
|  * Typedefs | |
|  */ | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|  | |
| typedef pthread_t   thread_handle_t; | |
| 
 | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
|  | |
| typedef HANDLE      thread_handle_t; | |
| 
 | |
| #else /* ? OS */ | |
|  | |
| # error Operating system not discriminated | |
|  | |
| #endif /* OS */ | |
|  | |
| typedef platformstl::filesystem_traits<PAN_CHAR_T>  fs_traits_t; | |
| typedef platformstl::basic_file_lines<PAN_CHAR_T>   lines_t; | |
| typedef stlsoft::basic_string_view<PAN_CHAR_T>          string_view_t; | |
| 
 | |
| /* ///////////////////////////////////////////////////////////////////////// | |
|  * Forward declarations | |
|  */ | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|  | |
| static void* thread_proc(void*); | |
| 
 | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
|  | |
| static DWORD WINAPI thread_proc(void*); | |
| 
 | |
| #else /* ? OS */ | |
|  | |
| # error Operating system not discriminated | |
|  | |
| #endif /* OS */ | |
|  | |
| /* ///////////////////////////////////////////////////////////////////////// | |
|  * Pantheios configuration | |
|  */ | |
| 
 | |
| enum | |
| { | |
|     beid_console    =   1, | |
|     beid_file       =   2, | |
| }; | |
| 
 | |
| extern "C" | |
| { | |
| 
 | |
| pan_fe_N_t PAN_FE_N_SEVERITY_CEILINGS[] = | |
| { | |
| #ifdef _DEBUG | |
|     { beid_console, PANTHEIOS_SEV_NOTICE }, | |
| #else /* ? _DEBUG */ | |
|     { beid_console, PANTHEIOS_SEV_NOTICE }, | |
| #endif /* _DEBUG */ | |
|     { beid_file, PANTHEIOS_SEV_INFORMATIONAL }, | |
| 
 | |
|     PANTHEIOS_FE_N_TERMINATOR_ENTRY(PANTHEIOS_SEV_INFORMATIONAL) | |
| }; | |
| 
 | |
| pan_be_N_t PAN_BE_N_BACKEND_LIST[] = | |
| { | |
|     PANTHEIOS_BE_N_STDFORM_ENTRY(beid_console, pantheios_be_console, PANTHEIOS_BE_N_F_IGNORE_NONMATCHED_CUSTOM28_ID), | |
|     PANTHEIOS_BE_N_STDFORM_ENTRY(beid_file, pantheios_be_file, PANTHEIOS_BE_N_F_ID_MUST_MATCH_CUSTOM28), | |
| 
 | |
|     PANTHEIOS_BE_N_TERMINATOR_ENTRY | |
| }; | |
| 
 | |
| } // extern "C" | |
|  | |
| /* ////////////////////////////////////////////////////////////////////// */ | |
| 
 | |
| static int main_(int /*argc*/, char** /*argv*/) | |
| { | |
|     thread_handle_t   threads[32]; | |
| 
 | |
|     pan::log_INFORMATIONAL(PSTR("main(): creating "), pan::integer(STLSOFT_NUM_ELEMENTS(threads)), PSTR(" threads")); | |
| 
 | |
|     { for(size_t i = 0; i < STLSOFT_NUM_ELEMENTS(threads); ++i) | |
|     { | |
|         void* arg = NULL; | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|  | |
|         pthread_mutex_lock(&s_mx); | |
| 
 | |
|         if(0 != pthread_create(&threads[i], NULL, thread_proc, arg)) | |
|         { | |
|             pan::log_ALERT(PSTR("Failed to create thread "), pan::integer(i)); | |
| 
 | |
|             pthread_mutex_unlock(&s_mx); | |
| 
 | |
|             return EXIT_FAILURE; | |
|         } | |
|         else | |
|         { | |
|             pthread_detach(threads[i]); | |
|             ++s_activeThreads; | |
|         } | |
| 
 | |
|         pthread_mutex_unlock(&s_mx); | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
|  | |
|         // NOTE: We are calling the Windows API function CreateThread() | |
|         // directly, rather than going through beginthreadex() (or | |
|         // equivalent), which is not the correct thing to do in a real | |
|         // program, as CRT resources can be leaked. | |
|         // | |
|         // We do it this way in this case simply for portability of the | |
|         // test program | |
|  | |
|         DWORD   threadId; | |
| 
 | |
|         threads[i] = CreateThread(NULL, 0, thread_proc, arg, 0, &threadId); | |
| 
 | |
|         if(NULL == threads[i]) | |
|         { | |
|             winstl::error_desc  err; | |
| 
 | |
|             pan::log_ALERT(PSTR("Failed to create thread "), pan::integer(i), PSTR(": "), err); | |
| 
 | |
|             return EXIT_FAILURE; | |
|         } | |
| 
 | |
| #else /* ? OS */ | |
| # error Operating system not discriminated | |
| #endif /* 0 */ | |
|     }} | |
| 
 | |
|     s_showNotices && | |
|     pan::log_NOTICE(PSTR("main(): all "), pan::integer(STLSOFT_NUM_ELEMENTS(threads)), PSTR(" threads started")); | |
| 
 | |
|     platformstl::micro_sleep(SET_PATH_DELAY); | |
| 
 | |
|     s_showNotices && | |
|     pan::log_NOTICE(PSTR("main(): setting log file path")); | |
| 
 | |
|     pantheios_be_file_setFilePath(LOG_FILE_NAME, PANTHEIOS_BE_FILE_F_TRUNCATE, PANTHEIOS_BE_FILE_F_TRUNCATE, 0); | |
| 
 | |
|     s_showNotices && | |
|     pan::log_NOTICE(PSTR("main(): waiting for threads to complete; this could take several minutes")); | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|  | |
|     for(;;) | |
|     { | |
|         pthread_mutex_lock(&s_mx); | |
|         if(0 == s_activeThreads) | |
|         { | |
|             pthread_mutex_unlock(&s_mx); | |
|             break; | |
|         } | |
|         else | |
|         { | |
|             pthread_cond_wait(&s_cv, &s_mx); | |
|         } | |
|         pthread_mutex_unlock(&s_mx); | |
|     } | |
| 
 | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
|  | |
|     ::WaitForMultipleObjects(STLSOFT_NUM_ELEMENTS(threads), &threads[0], true, INFINITE); | |
| 
 | |
| #else /* ? OS */ | |
| # error Operating system not discriminated | |
| #endif /* 0 */ | |
|  | |
|     s_showNotices && | |
|     pan::log_NOTICE(PSTR("main(): all "), pan::integer(STLSOFT_NUM_ELEMENTS(threads)), PSTR(" threads completed")); | |
| 
 | |
|     pantheios_be_file_setFilePath(NULL); | |
| 
 | |
|     int retVal = EXIT_SUCCESS; | |
| 
 | |
|     lines_t lines(LOG_FILE_NAME); | |
| 
 | |
|     { for(size_t i = 0; i != lines.size(); ++i) | |
|     { | |
|         lines_t::value_type const&  line = lines[i]; | |
| 
 | |
|         string_view_t               scratch1; | |
|         string_view_t               scratch2; | |
|                                      | |
|         string_view_t               prefix; | |
|         string_view_t               left; | |
|         string_view_t               middle; | |
|         string_view_t               right; | |
| 
 | |
|         if( !stlsoft::split(line, PSTR('|'), prefix, scratch1) || | |
|             !stlsoft::split(scratch1, PSTR('|'), left, scratch2) || | |
|             !stlsoft::split(scratch2, PSTR('|'), middle, right)) | |
|         { | |
|             pan::log_CRITICAL(PSTR("line does not have required format: ["), pan::integer(i), PSTR(": "), line, PSTR("]")); | |
| 
 | |
|             retVal = EXIT_FAILURE; | |
|         } | |
|         else | |
|         { | |
|             if(left != right) | |
|             { | |
|                 pan::log_CRITICAL(PSTR("line prefix and suffix do not match: ["), pan::integer(i), PSTR(": "), line, PSTR("]")); | |
| 
 | |
|                 retVal = EXIT_FAILURE; | |
|             } | |
|         } | |
|     }} | |
| 
 | |
|     if(EXIT_SUCCESS == retVal) | |
|     { | |
|         s_showNotices && | |
|         pan::log_NOTICE(PSTR("all lines logged to file correctly")); | |
|     } | |
|     else | |
|     { | |
|         pan::log_CRITICAL(PSTR("multi-threaded use of be.file has been defective; please report to the Pantheios project")); | |
|     } | |
| 
 | |
|     return retVal; | |
| } | |
| 
 | |
| #ifdef USE_MSC_VER_CRT_MEM_CHECKS | |
| int main__(int argc, char** argv) | |
| { | |
|     _CrtMemState memState; | |
| 
 | |
|     _CrtMemCheckpoint(&memState); | |
| 
 | |
|     int r = main_(argc, argv); | |
| 
 | |
|     _CrtMemDumpAllObjectsSince(&memState); | |
| 
 | |
|     return r; | |
| } | |
| #else /* ? USE_MSC_VER_CRT_MEM_CHECKS */ | |
| # define main__     main_ | |
| #endif /* USE_MSC_VER_CRT_MEM_CHECKS */ | |
|  | |
| 
 | |
| int main(int argc, char** argv) | |
| { | |
|     if(2 == argc) | |
|     { | |
|         char const* arg = argv[1]; | |
| 
 | |
|         if(0 == ::strncmp(arg, "--verbosity=", 12)) | |
|         { | |
|             int verbosity = atoi(arg + 12); | |
| 
 | |
|             if(verbosity < 0) | |
|             { | |
|                 verbosity = 0; | |
|             } | |
| 
 | |
|             switch(verbosity) | |
|             { | |
|                 case    0: | |
|                 case    1: | |
|                     s_showNotices = false; | |
|                     break; | |
|                 default: | |
|                     s_showNotices = true; | |
|                     break; | |
|             } | |
|         } | |
|         else | |
|         { | |
|             fprintf(stderr, "unrecognised argument: '%s'\n", arg); | |
| 
 | |
|             return EXIT_FAILURE; | |
|         } | |
|     } | |
| 
 | |
| #ifdef STLSOFT_CF_EXCEPTION_SUPPORT | |
|     try | |
|     { | |
| #endif /* !STLSOFT_CF_EXCEPTION_SUPPORT */ | |
|  | |
|         int r = main_(argc, argv); | |
| 
 | |
|         fs_traits_t::unlink_file(LOG_FILE_NAME); | |
| 
 | |
|         return r; | |
| 
 | |
| #ifdef STLSOFT_CF_EXCEPTION_SUPPORT | |
|     } | |
|     catch(std::exception& x) | |
|     { | |
|         pan::log_ALERT(PSTR("Unexpected general error: "), pantheios::exception(x), PSTR(". Application terminating")); | |
|     } | |
|     catch(...) | |
|     { | |
|         pan::logputs(pan::emergency, PSTR("Unhandled unknown error")); | |
|     } | |
| #endif /* !STLSOFT_CF_EXCEPTION_SUPPORT */ | |
|  | |
|     fs_traits_t::unlink_file(LOG_FILE_NAME); | |
| 
 | |
|     return EXIT_FAILURE; | |
| } | |
| 
 | |
| /* ////////////////////////////////////////////////////////////////////// */ | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
| static void* thread_proc(void*) | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
| static DWORD WINAPI thread_proc(void*) | |
| #endif /* OS */ | |
| { | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|     thread_handle_t self = pthread_self(); | |
| #elif defined(PLATFORMSTL_OS_IS_WINDOWS) | |
|     thread_handle_t self = GetCurrentThread(); | |
| #endif /* OS */ | |
|  | |
|     pan::log_INFORMATIONAL(PSTR("thread_proc("), pan::blob(&self, sizeof(self)), PSTR("): entering")); | |
| 
 | |
|     // TODO: Do some threading stuff | |
|  | |
|     { for(size_t i = 0; i != LOG_ITERATIONS; ++i) | |
|     { | |
|         pan::log(pan::informational(beid_file), PSTR("|"), pan::integer(i), PSTR("|"), PSTR("this is"), PSTR(" "), PSTR("multipart log "), PSTR("statement"), PSTR("|"), pan::integer(i)); | |
|     }} | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) | |
|     pthread_mutex_lock(&s_mx); | |
|     --s_activeThreads; | |
|     pthread_cond_signal(&s_cv); | |
|     pthread_mutex_unlock(&s_mx); | |
| #endif /* OS */ | |
|  | |
|     pan::log_INFORMATIONAL(PSTR("thread_proc("), pan::blob(&self, sizeof(self)), PSTR("): exiting")); | |
| 
 | |
|     return 0; | |
| } | |
| 
 | |
| /* ////////////////////////////////////////////////////////////////////// */ | |
| 
 | |
| #if defined(PLATFORMSTL_OS_IS_UNIX) && \ | |
|     defined(_WIN32) && \ | |
|     defined(_STLSOFT_FORCE_ANY_COMPILER) | |
| # include <windows.h> | |
| extern "C" void syslog(char const* s) | |
| { | |
|     OutputDebugStringA(s); | |
| } | |
| #endif | |
|  | |
| /* ///////////////////////////// end of file //////////////////////////// */
 |