diff options
author | William R. Otte <wotte@dre.vanderbilt.edu> | 2008-03-04 14:51:23 +0000 |
---|---|---|
committer | William R. Otte <wotte@dre.vanderbilt.edu> | 2008-03-04 14:51:23 +0000 |
commit | e60d54b4b03648afbb79382a1157238d4ffcc330 (patch) | |
tree | bda96bf8c3a4c2875a083d7b16720533c8ffeaf4 /ACE/tests | |
parent | dab6095386e31ea8302e88bb53b4735c6304e8c5 (diff) | |
download | ATCD-e60d54b4b03648afbb79382a1157238d4ffcc330.tar.gz |
undoing accidental deletion
Diffstat (limited to 'ACE/tests')
279 files changed, 83236 insertions, 0 deletions
diff --git a/ACE/tests/ACE.bsp b/ACE/tests/ACE.bsp new file mode 100644 index 00000000000..58959b48998 --- /dev/null +++ b/ACE/tests/ACE.bsp @@ -0,0 +1,25 @@ +# Target description File for the Integrate utility for use with the +# INTEGRITY real-time operating system by Green Hills Software. +# Before editing this file, refer to your Integrate documentation. + +# ACE.bsp is only appropriate for dynamic download INTEGRITY applications +# built for use with ACE/TAO + +Target + MinimumAddress 0x0 + MaximumAddress 0x2fffffff + Clock StandardTick + EndClock + Clock HighResTimer + EndClock + Clock RealTimeClock + EndClock + InitialKernelObjects 50 + DefaultStartIt false + DefaultMaxPriority 255 + DefaultPriority 127 + DefaultWeight 255 + DefaultMaxWeight 255 + DefaultStackSize 0x10000 + DefaultMemoryRegionSize 0x100000 +EndTarget diff --git a/ACE/tests/ACE_Init_Test.cpp b/ACE/tests/ACE_Init_Test.cpp new file mode 100644 index 00000000000..89373bc32ba --- /dev/null +++ b/ACE/tests/ACE_Init_Test.cpp @@ -0,0 +1,129 @@ +// $Id$ + +// This is a Win32-only test for verifying that the ACE dll +// initializes itself without having a nonstatic object manager +// present by way of a console app's main function. It's a MFC dialog +// app - it pops up a dialog and spawns a thread - the thread will +// wait 2 seconds and programatically dismiss the dialog box. The +// main thread waits for the other one to exit, and that's the test. +// If the ACE DLL doesn't initialize correctly, it will go boom! +// +// This test program was initially generated from MSVC AppWizard, then +// some files were renamed and moved around to fit in with the ACE +// test directory structure. +// +// ACE_Init_Test.cpp : Defines the class behaviors for the application. + +#if !defined(ACE_HAS_MFC) + +#include "test_config.h" + +// If this is not a WIN32 platform do not even try to compile the +// test, many of the #includes make little sense. +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("ACE_Init_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("This is not a Win32 platform, test skipped\n"))); + ACE_END_TEST; + return 0; +} + +#else + +#include "ACE_Init_Test_StdAfx.h" +#include "ACE_Init_Test.h" +#include "ACE_Init_TestDlg.h" +#include "test_config.h" +#include "ace/ACE.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Thread_Manager.h" + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static ACE_THR_FUNC_RETURN wait_and_kill_dialog (void *pBox); + +///////////////////////////////////////////////////////////////////////////// +// CACE_Init_TestApp + +BEGIN_MESSAGE_MAP(CACE_Init_TestApp, CWinApp) + //{{AFX_MSG_MAP(CACE_Init_TestApp) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG + ON_COMMAND(ID_HELP, CWinApp::OnHelp) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CACE_Init_TestApp construction + +CACE_Init_TestApp::CACE_Init_TestApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CACE_Init_TestApp object + +CACE_Init_TestApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CACE_Init_TestApp initialization + +BOOL CACE_Init_TestApp::InitInstance() +{ + // This is needed because there's no overridden main(int, char *[]) + // which would normally handle the initialization. Also see the + // corresponding ACE::fini, below. + ACE::init(); + + ACE_START_TEST (ACE_TEXT ("ACE_Init_Test")); + + CACE_Init_TestDlg dlg; + m_pMainWnd = &dlg; + ACE_Thread_Manager::instance()->spawn (wait_and_kill_dialog, + m_pMainWnd); + int nResponse = dlg.DoModal(); + if (nResponse == IDOK) + { + // TODO: Place code here to handle when the dialog is + // dismissed with OK + } + else if (nResponse == IDCANCEL) + { + // TODO: Place code here to handle when the dialog is + // dismissed with Cancel + } + + ACE_Thread_Manager::instance()->wait(); + + ACE_END_TEST; + + // Since the dialog has been closed, return FALSE so that we exit the + // application, rather than start the application's message pump. + ACE::fini(); + + return FALSE; +} + +// This function runs in a separate thread - it will wait a couple of +// seconds and then programatically dismiss the dialog box. If ACE is +// not properly initialized, we will have crashed before getting here. +static ACE_THR_FUNC_RETURN +wait_and_kill_dialog (void *pBox) +{ + CACE_Init_TestDlg *pDialog = reinterpret_cast<CACE_Init_TestDlg *> (pBox); + ACE_OS::sleep(2); + pDialog->EndModalLoop (IDOK); + return 0; + +} + +#endif /* ACE_HAS_MFC */ diff --git a/ACE/tests/ACE_Init_Test.h b/ACE/tests/ACE_Init_Test.h new file mode 100644 index 00000000000..3916ff61b6f --- /dev/null +++ b/ACE/tests/ACE_Init_Test.h @@ -0,0 +1,59 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// ACE_Init_Test.h +// +// = DESCRIPTION +// Main header file for the ACE_INIT_TEST application. +// +// = AUTHOR +// Steve Huston <shuston@cs.wustl.edu> +// +// ============================================================================ + +#ifndef AFX_ACE_INIT_TEST_H__64FDC9FE_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_ +#define AFX_ACE_INIT_TEST_H__64FDC9FE_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "ACE_Init_Test_Resource.h" // main symbols + +class CACE_Init_TestApp : public CWinApp +{ + // = TITLE + // See ACE_Init_Test.cpp for the implementation of this class +public: + CACE_Init_TestApp (void); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CACE_Init_TestApp) +public: + virtual BOOL InitInstance(); + //}}AFX_VIRTUAL + + // Implementation + + //{{AFX_MSG(CACE_Init_TestApp) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif /* !defined(AFX_ACE_INIT_TEST_H__64FDC9FE_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_) */ diff --git a/ACE/tests/ACE_Init_Test.ico b/ACE/tests/ACE_Init_Test.ico Binary files differnew file mode 100644 index 00000000000..7eef0bcbe65 --- /dev/null +++ b/ACE/tests/ACE_Init_Test.ico diff --git a/ACE/tests/ACE_Init_Test.rc b/ACE/tests/ACE_Init_Test.rc new file mode 100644 index 00000000000..647f04a1ba6 --- /dev/null +++ b/ACE/tests/ACE_Init_Test.rc @@ -0,0 +1,169 @@ +//Microsoft Developer Studio generated resource script. +// +#include "ACE_Init_Test_Resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""res\\ACE_Init_Test.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#endif\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "ACE_Init_Test.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +#ifdef APSTUDIO_INVOKED +IDD_ACE_INIT_TEST_DIALOG DIALOGEX 0, 0, 185, 92 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "ACE_Init_Test" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,128,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,128,23,50,14 + LTEXT "This box will auto-dismiss.",IDC_STATIC,13,7,113, + 8 +END +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "ACE_Init_Test MFC Application\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "ACE_Init_Test\0" + VALUE "LegalCopyright", "Copyright (C) 1999\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "ACE_Init_Test.EXE\0" + VALUE "ProductName", "ACE_Init_Test Application\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ACE_INIT_TEST_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 178 + TOPMARGIN, 7 + BOTTOMMARGIN, 85 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif +#include "ACE_Init_Test.rc2" // non-Microsoft Visual C++ edited resources +#endif +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/ACE/tests/ACE_Init_Test.rc2 b/ACE/tests/ACE_Init_Test.rc2 new file mode 100644 index 00000000000..91f5498d5c9 --- /dev/null +++ b/ACE/tests/ACE_Init_Test.rc2 @@ -0,0 +1,13 @@ +// +// ACE_INIT_TEST.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/ACE/tests/ACE_Init_TestDlg.cpp b/ACE/tests/ACE_Init_TestDlg.cpp new file mode 100644 index 00000000000..9b10c1e0257 --- /dev/null +++ b/ACE/tests/ACE_Init_TestDlg.cpp @@ -0,0 +1,117 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This is a Win32-only test for verifying that the ACE dll +// initializes itself without having a nonstatic object manager +// present by way of a console app's main function. It's a MFC dialog +// app - it pops up a dialog and spawns a thread - this source file is +// the code for implementing the dialog box. +// +// This test program was initially generated from MSVC AppWizard, then +// some files were renamed and moved around to fit in with the ACE +// test directory structure. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#if defined(ACE_HAS_MFC) + +#include "ACE_Init_Test_StdAfx.h" +#include "ACE_Init_Test.h" +#include "ACE_Init_TestDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CACE_Init_TestDlg dialog + +CACE_Init_TestDlg::CACE_Init_TestDlg(CWnd* pParent /*=NULL*/) + : CDialog(CACE_Init_TestDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CACE_Init_TestDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); +} + +void CACE_Init_TestDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CACE_Init_TestDlg) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CACE_Init_TestDlg, CDialog) + //{{AFX_MSG_MAP(CACE_Init_TestDlg) + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CACE_Init_TestDlg message handlers + +BOOL CACE_Init_TestDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // Set the icon for this dialog. The framework does this automatically + // when the application's main window is not a dialog + SetIcon(m_hIcon, TRUE); // Set big icon + SetIcon(m_hIcon, FALSE); // Set small icon + + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control +} + +// If you add a minimize button to your dialog, you will need the code below +// to draw the icon. For MFC applications using the document/view model, +// this is automatically done for you by the framework. + +void CACE_Init_TestDlg::OnPaint() +{ + if (IsIconic()) + { + CPaintDC dc(this); // device context for painting + + SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); + + // Center icon in client rectangle + int cxIcon = GetSystemMetrics(SM_CXICON); + int cyIcon = GetSystemMetrics(SM_CYICON); + CRect rect; + GetClientRect(&rect); + int x = (rect.Width() - cxIcon + 1) / 2; + int y = (rect.Height() - cyIcon + 1) / 2; + + // Draw the icon + dc.DrawIcon(x, y, m_hIcon); + } + else + { + CDialog::OnPaint(); + } +} + +// The system calls this to obtain the cursor to display while the user drags +// the minimized window. +HCURSOR CACE_Init_TestDlg::OnQueryDragIcon() +{ + return (HCURSOR) m_hIcon; +} + +#endif /* ACE_HAS_MFC */ diff --git a/ACE/tests/ACE_Init_TestDlg.h b/ACE/tests/ACE_Init_TestDlg.h new file mode 100644 index 00000000000..94dbac7ecbb --- /dev/null +++ b/ACE/tests/ACE_Init_TestDlg.h @@ -0,0 +1,47 @@ +// $Id$ +// ACE_Init_TestDlg.h : header file + +#if !defined(AFX_ACE_INIT_TESTDLG_H__64FDCA00_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_) +#define AFX_ACE_INIT_TESTDLG_H__64FDCA00_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +// CACE_Init_TestDlg dialog + +class CACE_Init_TestDlg : public CDialog +{ +// Construction +public: + CACE_Init_TestDlg(CWnd* pParent = 0); // standard constructor + +// Dialog Data + //{{AFX_DATA(CACE_Init_TestDlg) + enum { IDD = IDD_ACE_INIT_TEST_DIALOG }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CACE_Init_TestDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + HICON m_hIcon; + + // Generated message map functions + //{{AFX_MSG(CACE_Init_TestDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnPaint(); + afx_msg HCURSOR OnQueryDragIcon(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif /* !defined(AFX_ACE_INIT_TESTDLG_H__64FDCA00_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_) */ diff --git a/ACE/tests/ACE_Init_Test_Resource.h b/ACE/tests/ACE_Init_Test_Resource.h new file mode 100644 index 00000000000..102cdab13e3 --- /dev/null +++ b/ACE/tests/ACE_Init_Test_Resource.h @@ -0,0 +1,19 @@ +// $Id$ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ACE_INIT_TEST.RC +// +#define IDR_MAINFRAME 128 +#define IDD_ACE_INIT_TEST_DIALOG 102 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/ACE/tests/ACE_Init_Test_StdAfx.h b/ACE/tests/ACE_Init_Test_StdAfx.h new file mode 100644 index 00000000000..9aa682148ef --- /dev/null +++ b/ACE/tests/ACE_Init_Test_StdAfx.h @@ -0,0 +1,44 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// ACE_Init_Test_StdAfx.h +// +// = DESCRIPTION +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// +// = AUTHOR +// Steve Huston <shuston@cs.wustl.edu> +// +// ============================================================================ + +#ifndef AFX_STDAFX_H__64FDCA02_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_ +#define AFX_STDAFX_H__64FDCA02_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +// Exclude rarely-used stuff from Windows headers +#define VC_EXTRALEAN + +// MFC core and standard components +#include <afxwin.h> +// MFC extensions +#include <afxext.h> +#ifndef _AFX_NO_AFXCMN_SUPPORT +// MFC support for Windows Common Controls +#include <afxcmn.h> +#endif /* _AFX_NO_AFXCMN_SUPPORT */ + +//{{AFX_INSERT_LOCATION}} Microsoft Developer Studio will insert +//additional declarations immediately before the previous line. + +#endif /* !defined(AFX_STDAFX_H__64FDCA02_F7F9_11D2_89B6_00A024CC68DB__INCLUDED_) */ diff --git a/ACE/tests/ACE_Test.cpp b/ACE/tests/ACE_Test.cpp new file mode 100644 index 00000000000..86749cc3a0c --- /dev/null +++ b/ACE/tests/ACE_Test.cpp @@ -0,0 +1,104 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This simple test exercises and illustrates use of ACE value-added +// functions. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" + +#include "ace/ACE.h" + +ACE_RCSID(tests, ACE_Test, "$Id$") + +int +log2_test (void) +{ + u_long values[] = {1, 2, 4, 8, 1048576}; + u_long results[] = {0, 1, 2, 3, 20}; + u_long result = 0; + int error_count = 0; + + for (size_t i = 0 ; i < sizeof (values) / sizeof (u_long) ; i++) + { + result = ACE::log2(values [i]); + if (result != results [i]) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Log2 error: input %d, output %d, expected %d\n"), values [i], result, results [i])); + error_count++; + } + } + + return error_count; +} + +// Test ACE::execname to be sure it finds .exe without regard to case. +int +execname_test (void) +{ + int error_count = 0; + + // This test is only interesting on Win32 +#if defined (ACE_WIN32) + const ACE_TCHAR *newname; + const ACE_TCHAR *prog1 = ACE_TEXT ("myprog.exe"); + const ACE_TCHAR *prog2 = ACE_TEXT ("myprog.EXE"); + const ACE_TCHAR *prog3 = ACE_TEXT ("myprog"); + + newname = ACE::execname (prog1); + if (newname != prog1) // Didn't find .exe correctly + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Name %s, not %s\n"), newname, prog1)); + delete [] const_cast<ACE_TCHAR *> (newname); + ++error_count; + } + + newname = ACE::execname (prog2); + if (newname != prog2) // Didn't find .exe correctly + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Name %s, not %s\n"), newname, prog2)); + delete [] const_cast<ACE_TCHAR *> (newname); + ++error_count; + } + + newname = ACE::execname (prog3); + if (newname == prog3) // Thought the name didn't need .exe + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Says .exe not needed for %s\n"), + newname)); + ++error_count; + } + else + delete [] const_cast<ACE_TCHAR *> (newname); +#endif /* ACE_WIN32 */ + + return error_count; +} + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("ACE_Test")); + + int status = 0; + int result; + + if ((result = execname_test ()) != 0) + status = result; + + if ((result = log2_test ()) != 0) + status = result; + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/ARGV_Test.cpp b/ACE/tests/ARGV_Test.cpp new file mode 100644 index 00000000000..0e7e18f52d8 --- /dev/null +++ b/ACE/tests/ARGV_Test.cpp @@ -0,0 +1,277 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This simple test illustrates how to use advanced features of +// <ACE_ARGV>. +// +// = AUTHOR +// Suresh Kannan <kannan@uav.ae.gatech.edu> and +// Duane Binder <duane.binder@veritas.com> + +// ============================================================================ + +#include "ace/ARGV.h" +#include "ace/Arg_Shifter.h" +#include "ace/OS_NS_string.h" +#include "ace/Argv_Type_Converter.h" +#include "test_config.h" + +ACE_RCSID (tests, ARGV_Test, "$Id$") + +static void +consume_arg (int argc, ACE_TCHAR *argv[]) +{ + ACE_Arg_Shifter arg_shifter (argc, argv); + + if (arg_shifter.is_anything_left ()) + arg_shifter.consume_arg (1); + // Once we initialize an arg_shifter, we must iterate through it all! + while ((arg_shifter.is_anything_left ())) + arg_shifter.ignore_arg (1); +} + +static int +test_simple_argv (ACE_TCHAR *argv[]) +{ + // From command line. + ACE_ARGV cl (argv); + + // My own stuff. + ACE_ARGV my; + + // Add to my stuff. + my.add (ACE_TEXT ("-ORBEndpoint iiop://localhost:12345")); + + // Combine the two (see the ace/ARGV.h constructors documentation). + ACE_ARGV a (cl.argv (), + my.argv ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("argc = %d\n"), + a.argc ())); + + // Print the contents of the combined <ACE_ARGV>. + for (int i = 0; i < a.argc (); i++) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%d) %s\n"), + i, + a.argv ()[i])); + + return 0; +} + +static int +test_argv_type_converter (void) +{ + char *argv[20]; + argv[0] = ACE_OS_String::strdup ("one"); + argv[1] = ACE_OS_String::strdup ("two"); + argv[2] = ACE_OS_String::strdup ("three"); + argv[3] = ACE_OS_String::strdup ("four"); + argv[4] = 0; + + char *save_argv[20]; + ACE_OS_String::memcpy (save_argv, argv, sizeof (argv)); + + int argc = 4; + + { + ACE_Argv_Type_Converter ct2 (argc, argv); + } + + { + ACE_Argv_Type_Converter ct (argc, argv); + ct.get_argc (); ct.get_TCHAR_argv (); + consume_arg ( ct.get_argc (), ct.get_TCHAR_argv ()); + } + { + ACE_Argv_Type_Converter ct3 (argc, argv); + ct3.get_argc (); ct3.get_ASCII_argv (); + consume_arg ( ct3.get_argc (), ct3.get_TCHAR_argv ()); + } + + { + for (size_t i = 0; i < 4; i++) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%d) %s\n"), + i, + ACE_TEXT_CHAR_TO_TCHAR (argv[i]))); + } + + for (size_t i = 0; save_argv[i]; ++i) + ACE_OS_Memory::free (save_argv[i]); + + return 0; +} + +static int +test_argv_type_converter2 (void) +{ + ACE_TCHAR *argv[20]; + argv[0] = ACE_OS_String::strdup (ACE_TEXT ("one")); + argv[1] = ACE_OS_String::strdup (ACE_TEXT ("two")); + argv[2] = ACE_OS_String::strdup (ACE_TEXT ("three")); + argv[3] = ACE_OS_String::strdup (ACE_TEXT ("four")); + argv[4] = 0; + + ACE_TCHAR *save_argv[20]; + ACE_OS_String::memcpy (save_argv, argv, sizeof (argv)); + + int argc = 4; + + { + ACE_Argv_Type_Converter ct (argc, argv); + ct.get_argc (); ct.get_TCHAR_argv (); + consume_arg ( ct.get_argc (), ct.get_TCHAR_argv ()); + } + + consume_arg (argc, argv); + + { + for (size_t i = 0; i < 4; i++) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%d) %s\n"), + i, + argv[i])); + } + + for (size_t i = 0; save_argv[i]; ++i) + ACE_OS_Memory::free (save_argv[i]); + + return 0; +} + +static int +test_argv_buf (void) +{ + pid_t parent_pid = ACE_OS::getpid (); + + ACE_TCHAR *l_argv[3]; + ACE_TCHAR pid_str[100]; + // Store the parent's process id so we can pass it to the child + // portably. Also, pass the test number, as well. + ACE_OS::sprintf (pid_str, + ACE_TEXT ("-p %ld -t %d"), + static_cast <long> (parent_pid), + 1); + + // We're going to create a new process that runs this program again, + // so we need to indicate that it's the child. + const ACE_TCHAR *t = ACE_TEXT (".") + ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("Signal_Test") + ACE_PLATFORM_EXE_SUFFIX + ACE_TEXT (" -c"); + l_argv[0] = const_cast <ACE_TCHAR *> (t); + l_argv[1] = pid_str; + l_argv[2] = 0; + + ACE_ARGV my_argv (l_argv); + + // This shouldn't have any quotes in it. + ACE_DEBUG ((LM_DEBUG, "%s\n", my_argv.buf ())); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("argc: %d\n"), my_argv.argc ())); + return 0; +} + +static int +test_argv_quotes (void) +{ + const ACE_TCHAR *argv[] = { ACE_TEXT ("first without quotes"), + ACE_TEXT ("'second in single quotes'"), + ACE_TEXT ("\"third in double quotes\""), + 0 + }; + int argc = 3; + + // (argc, argv) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n*** argv ***\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("argc: %d\n"), argc)); + + for (int i = 0; i < argc; ++i) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("argv[%02d] = %s\n"), i, argv[i])); + + // args + ACE_ARGV args; + for (int i = 0; i < argc; ++i) + args.add (argv[i]); + args.add (ACE_TEXT ("'fourth in single quotes'")); + args.add (ACE_TEXT ("\"fifth in double quotes\"")); + args.add (ACE_TEXT ("sixth without any quotes")); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n*** args-1 ***\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args.argc ())); + + for (int i = 0; i < args.argc (); ++i) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args[%02d]: %s\n"), i, args[i])); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args.argc ())); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n*** args-2 ***\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args.argc ())); + + for (int i = 0; i < args.argc (); ++i) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argv ()[%02d]: %s\n"), + i, args.argv ()[i])); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args.argc ())); + + // args2 + ACE_ARGV args2; + for (int i = 0; i < argc; ++i) + args2.add (argv[i], true); + args2.add (ACE_TEXT ("'fourth in single quotes'"), true); + args2.add (ACE_TEXT ("\"fifth in double quotes\""), true); + args2.add (ACE_TEXT ("sixth without any quotes"), true); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n*** args-3 ***\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args2.argc ())); + + for (int i = 0; i < args2.argc (); ++i) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args[%02d]: %s\n"), i, args2[i])); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args2.argc ())); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n*** args-4 ***\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args2.argc ())); + + for (int i = 0; i < args2.argc (); ++i) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argv ()[%02d]: %s\n"), + i, args2.argv ()[i])); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args2.argc ())); + + // args3 + ACE_ARGV args3(argv); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n*** args-5 ***\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args3.argc ())); + + for (int i = 0; i < args3.argc (); ++i) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args[%02d]: %s\n"), i, args3[i])); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("args.argc (): %d\n"), args3.argc ())); + + return 0; +} + +int +run_main (int, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("ARGV_Test")); + + test_simple_argv (argv); + test_argv_type_converter2 (); + test_argv_type_converter (); + test_argv_quotes (); + test_argv_buf (); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Aio_Platform_Test.cpp b/ACE/tests/Aio_Platform_Test.cpp new file mode 100644 index 00000000000..22c13223e06 --- /dev/null +++ b/ACE/tests/Aio_Platform_Test.cpp @@ -0,0 +1,198 @@ +// $Id$ + +// ============================================================================ +// +// = FILENAME +// aio_platform_test.cpp +// +// = DESCRITPTION +// Testing the platform for POSIX Asynchronous I/O. Basically +// prints the predefined constants and also checks for their run +// time values. If this test succeeds further tests at +// $ACE_ROOT/examples/Reactor/Proactor can be used to test the +// features further. +// +// = AUTHOR +// Programming for the Real World. Bill O. GallMeister. Modified +// by Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ===================================================================== + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Aio_Platform_Test, "$Id$") + +#if defined (_POSIX_ASYNCHRONOUS_IO) +static int do_sysconf (void) +{ + ACE_DEBUG ((LM_DEBUG, + "Doing <sysconf> calls to know the run-time values of POSIX feature limits\n")); + + // Call sysconf to find out runtime values. + errno = 0; +#if defined (_SC_LISTIO_AIO_MAX) + ACE_DEBUG ((LM_DEBUG, + "Runtime value of LISTIO_AIO_MAX is %d, errno = %d, Minimum is 2\n", + ACE_OS::sysconf (_SC_LISTIO_AIO_MAX), + errno)); +#elif defined (_SC_AIO_LISTIO_MAX) + ACE_DEBUG ((LM_DEBUG, + "Runtime value of AIO_LISTIO_MAX is %d, errno = %d, Minimum is 2\n", + ACE_OS::sysconf (_SC_AIO_LISTIO_MAX), + errno)); +#else + ACE_ERROR ((LM_ERROR, + "_SC_LISTIO_AIO_MAX or _SC_AIO_LISTIO_MAX" + " do not exist on this platform\n")); +#endif /* _SC_LISTIO_AIO_MAX */ + +#if defined (_SC_AIO_MAX) + errno = 0; + ACE_DEBUG ((LM_DEBUG, + "Runtime value of AIO_MAX is %d, errno = %d, Minimum is 1\n", + ACE_OS::sysconf (_SC_AIO_MAX), + errno)); +#else + ACE_ERROR ((LM_ERROR, + "_SC_AIO_MAX does not exist on this platform\n")); +#endif /* _SC_AIO_MAX */ + +#if defined (_SC_ASYNCHRONOUS_IO) + errno = 0; + ACE_DEBUG ((LM_DEBUG, + "Runtime value of _POSIX_ASYNCHRONOUS_IO is %d, errno = %d\n", + ACE_OS::sysconf (_SC_ASYNCHRONOUS_IO), + errno)); +#else /* Not _SC_ASYNCHRONOUS_IO */ + ACE_ERROR ((LM_ERROR, + "_SC_ASYNCHRONOUS_IO does not exist on this platform\n")); +#endif /* _SC_ASYNCHRONOUS_IO */ + +#if defined (_SC_REALTIME_SIGNALS) + errno = 0; + ACE_DEBUG ((LM_DEBUG, + "Runtime value of _POSIX_REALTIME_SIGNALS is %d, errno = %d\n", + ACE_OS::sysconf (_SC_REALTIME_SIGNALS), + errno)); +#else /* Not _SC_REALTIME_SIGNALS */ + ACE_ERROR ((LM_ERROR, + "_SC_REALTIME_SIGNALS does not exist on this platform\n")); +#endif /* _SC_REALTIME_SIGNALS */ + + +#if defined (_SC_RTSIG_MAX) + errno = 0; + ACE_DEBUG ((LM_DEBUG, + "Runtime value of RTSIG_MAX %d, Errno = %d, Minimum is 8\n", + ACE_OS::sysconf (_SC_RTSIG_MAX), + errno)); +#else /* Not _SC_RTSIG_MAX */ + ACE_ERROR ((LM_ERROR, + "_SC_RTSIG_MAX does not exist on this platform\n")); +#endif /* _SC_RTSIG_MAX */ + +#if defined (_SC_SIGQUEUE_MAX) + errno = 0; + ACE_DEBUG ((LM_DEBUG, + "Runtime value of SIGQUEUE_MAX %d, Errno = %d, Minimum is 32\n", + ACE_OS::sysconf (_SC_SIGQUEUE_MAX), + errno)); +#else /* Not _SC_SIGQUEUE_MAX */ + ACE_ERROR ((LM_ERROR, + "_SC_SIGQUEUE_MAX does not exist on this platform\n")); +#endif /* _SC_SIGQUEUE_MAX */ + return 0; +} +#endif /* _POSIX_ASYNCHRONOUS_IO */ + +static int +have_asynchio (void) +{ +#if defined (_POSIX_ASYNCHRONOUS_IO) +#if defined (_POSIX_ASYNC_IO) +#if _POSIX_ASYNC_IO == -1 + ACE_DEBUG ((LM_DEBUG, + "_POSIX_ASYNC_IO = -1.. ASYNCH IO NOT supported at all\n")); + return -1; +#else /* Not _POSIX_ASYNC_IO == -1 */ + ACE_DEBUG ((LM_DEBUG, + "_POSIX_ASYNC_IO = %d\n ASYNCH IO is supported FULLY\n", + _POSIX_ASYNC_IO)); +#endif /* _POSIX_ASYNC_IO == -1 */ + +#else /* Not defined _POSIX_ASYNC_IO */ + ACE_ERROR ((LM_DEBUG, + "_POSIX_ASYNC_IO is not defined.\n")); + ACE_DEBUG ((LM_DEBUG, + "AIO might *not* be supported on all the paths\n")); +#endif /* _POSIX_ASYNC_IO */ + + // System defined POSIX Values. + ACE_DEBUG ((LM_DEBUG, + "System claims to have POSIX_ASYNCHRONOUS_IO\n")); + +#if defined(_POSIX_AIO_LISTIO_MAX) + ACE_DEBUG ((LM_DEBUG, + "Number of operations in one listio: " + "Minimum value is 2: " + "_POSIX_AIO_LISTIO_MAX = %d\n", + _POSIX_AIO_LISTIO_MAX)); +#else + ACE_DEBUG ((LM_DEBUG, + "No value for _POSIX_AIO_LISTIO_MAX\n")); +#endif + +#if defined(_POSIX_AIO_MAX) + ACE_DEBUG ((LM_DEBUG, + "Number of simultaneous asynchronous I/Os: " + "Minimum is 1: " + "_POSIX_AIO_MAX = %d\n", + _POSIX_AIO_MAX)); +#else + ACE_DEBUG ((LM_DEBUG, + "No value for _POSIX_AIO_MAX\n")); +#endif + + // @@ Debugging. + ACE_DEBUG ((LM_DEBUG, + "Before do_sysconf : Errno %d\n", + errno)); + + // Check and print the run time values. + do_sysconf (); + + // @@ Debugging. + ACE_DEBUG ((LM_DEBUG, + "After do_sysconf: Errno : %d\n", errno)); + + return 0; + +#else /* Not _POSIX_ASYNCHRONOUS_IO */ + ACE_DEBUG ((LM_DEBUG, + "No support._POSIX_ASYNCHRONOUS_IO itself is not defined\n")); + return -1; +#endif /* _POSIX_ASYNCHRONOUS_IO */ +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Aio_Platform_Test")); + + // Test the #defined and constants and runtime values. + errno = 0; + if (have_asynchio () == 0) + ACE_DEBUG ((LM_DEBUG, + "Basic test successful" + "Check the run time values of the predefined constants\n" + "ACE_HAS_AIO_CALLS can be defined for this platform\n" + "Further tests at $ACE_ROOT/examples/Reactor/Proactor\n")); + else + ACE_ERROR ((LM_INFO, + "AIO not supported on this platform\n")); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Arg_Shifter_Test.cpp b/ACE/tests/Arg_Shifter_Test.cpp new file mode 100644 index 00000000000..649f8054f46 --- /dev/null +++ b/ACE/tests/Arg_Shifter_Test.cpp @@ -0,0 +1,43 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program exercises the ACE_Arg_Shifter class. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "ace/Arg_Shifter.h" +#include "test_config.h" + +ACE_RCSID(tests, Arg_Shifter_Test, "$Id$") + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Arg_Shifter_Test")); + + const int test_argc_size = 5; + int argl (test_argc_size); + const ACE_TCHAR *args[test_argc_size] = { + ACE_TEXT ("-known"), + ACE_TEXT ("-huh"), + ACE_TEXT ("-arg"), + ACE_TEXT ("-what"), + ACE_TEXT ("arg") + }; + + ACE_Arg_Shifter shifter (argl, args); + + if (!shifter.is_anything_left ()) + ACE_ERROR ((LM_ERROR, "is_anything_left() returned 0 at start.\n")); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Array_Map_Test.cpp b/ACE/tests/Array_Map_Test.cpp new file mode 100644 index 00000000000..9f5deeee3bd --- /dev/null +++ b/ACE/tests/Array_Map_Test.cpp @@ -0,0 +1,485 @@ +//============================================================================= +/** + * @file Array_Map_Test.cpp + * + * $Id$ + * + * Regression test for ACE_Array_Map. + * + * @author Ossama Othman + */ +//============================================================================= + + +#include "test_config.h" + +#include "ace/SString.h" +#include "ace/Array_Map.h" + +#include <algorithm> +//#include <map> /* For STL portability testing. */ + + +ACE_RCSID (tests, + Array_Map_Test, + "$Id$") + + +static char const letters[] = + { 'A', 'C', 'E', ' ', 'r', 'u', 'l', 'e', 'z', '!' }; + +static ACE_TString const words[] = + { + ACE_TEXT ("alpha"), // A + ACE_TEXT ("charlie"), // C + ACE_TEXT ("echo"), // E + ACE_TEXT (" "), // + ACE_TEXT ("romeo"), // r + ACE_TEXT ("uniform"), // u + ACE_TEXT ("lima"), // l + ACE_TEXT ("echo"), // e + ACE_TEXT ("zulu"), // z + ACE_TEXT ("!") // ! + }; + +static size_t const letters_len = sizeof (letters) / sizeof (letters[0]); +static size_t const words_len = sizeof (words) / sizeof (words[0]); + +// -------------------------------------------------------------- + +bool +insertion_removal_test (void) +{ + // Instantiate the map. + typedef ACE_Array_Map<char, ACE_TString> Map; + Map phonetic[2]; + + ACE_ASSERT (phonetic[0] == phonetic[1]); // Sanity check. + + static size_t const phonetic_len = + sizeof (phonetic) / sizeof (phonetic[0]); + + unsigned int count = 1; + + // Test map insertion. + for (Map * m = phonetic; + m != phonetic + phonetic_len; + ++m, ++count) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("------------- Populating map %u -------------\n"), + count)); + + + ACE_TString const * word = words; + for (char const * i = letters; + i != letters + letters_len; + ++i, ++word) + { + std::pair<Map::iterator, bool> const result = + m->insert (std::make_pair (*i, *word)); + + if (result.second) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Inserted \"%c | %s\"\n"), + *i, + word->c_str ())); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Insertion of \"%c | %s\" failed.\n"), + *i, + word->c_str ()), + false); + } + + ACE_ASSERT (m->size () == letters_len); + } + + // Test equality of identically populated maps. + if (!(phonetic[0] == phonetic[1])) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Equality comparison of two identical ") + ACE_TEXT ("maps failed.\n")), + false); + } + else if (phonetic[0] < phonetic[1]) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Relational comparison of two identical ") + ACE_TEXT ("maps incorrectly passed.\n")), + false); + } + + ACE_ASSERT (!phonetic[0].is_empty ()); // Sanity check. + + Map foo (phonetic[0]); // Copy construction + Map bar = foo; // Assignment + + if (!(foo == bar)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Second equality comparison of two ") + ACE_TEXT ("identical maps failed.\n")), + false); + } + + Map::value_type values[letters_len]; + size_t const values_len = sizeof (values) / sizeof (values[0]); + + ACE_TString const * word = words; + char const * letter = letters; + + for (Map::value_type * v = values; + v != values + values_len; + ++v, ++letter, ++word) + { + *v = std::make_pair (*letter, *word); + } + + Map A (values, values + values_len); + + if (!(A == bar)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Third equality comparison of two ") + ACE_TEXT ("identical maps failed.\n")), + false); + } + + Map B; + B.insert (values, values + values_len); + + if (!(A == B)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Fourth equality comparison of two ") + ACE_TEXT ("identical maps failed.\n")), + false); + } + + // ==== Removal tests ==== + + // Remove two elements from map. + Map::iterator letter_A = A.find ('A'); + ACE_ASSERT (letter_A == A.begin ()); // Should be first map element. + ACE_ASSERT (A.count ('A') == 1); // Should only be one letter 'A'. + + A.erase (letter_A); + if (A.find ('A') != A.end () + || A.count ('A') != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Letter '%c' not removed from map\n"), + (*letter_A).first), + false); + } + + static char const z = 'z'; + if (A.erase (z) != 1 + || A.count (z) != 0 + || A.find (z) != A.end ()) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Letter '%c' not removed from map\n"), + z), + false); + } + + + // Remove range of letters from map. + static size_t const removed_len = 3; + Map::iterator const first = B.begin () + 2; + Map::iterator const last = first + removed_len; + + ACE_ASSERT (static_cast<Map::size_type> (last - first) < B.size ()); + ACE_ASSERT (last < B.end ()); + + Map::value_type removed[removed_len]; + size_t rcount = 0; + for (Map::iterator x = first; x != last; ++x) + { + removed[rcount++] = *x; + } + + B.erase (first, last); + + for (size_t s = 0; s < removed_len ; ++s) + { + Map::key_type const key = removed[s].first; + + if (B.count (key) != 0 + || B.find (key) != B.end ()) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Letter '%c' from range of letters ") + ACE_TEXT ("not removed from map\n"), + key), + false); + } + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Insertion/removal test passed.\n"))); + + return true; +} + +// -------------------------------------------------------------- + +bool +index_operator_test (void) +{ + // Instantiate the map. + typedef ACE_Array_Map<char, ACE_TString> Map; + Map phonetic; + + ACE_ASSERT (phonetic.size () == 0 && phonetic.is_empty ()); + ACE_ASSERT (phonetic.max_size () > 1); + + // Run the same test twice, clearing the contents of the map between + // the iterations. The goal is to verify that the constant time + // clear() method performs as advertised. + for (unsigned int count = 1; count < 3; ++count) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("----- Index operator test ") + ACE_TEXT ("iteration %u -----\n"), + count)); + + // Test map insertion through the index operator. + ACE_TString const * word = words; + for (char const * i = letters; + i != letters + letters_len; + ++i, ++word) + { + phonetic[*i] = *word; + } + + ACE_ASSERT (phonetic.size () == letters_len); + + typedef Map::const_iterator const_iterator; + + // Access the elements that were inserted into the map. + char const * letter = letters; + word = words; + const_iterator const last = phonetic.end (); + for (const_iterator n = phonetic.begin (); + n != last; + ++n, ++letter, ++word) + { + if ((*n).first != *letter || (*n).second != *word) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Key/Datum mismatch:\n") + ACE_TEXT (" key \"%c\" should be \"%c\"\n") + ACE_TEXT (" datum \"%s\" should be \"%s\"\n"), + (*n).first, + *letter, + (*n).second.c_str (), + word->c_str ()), + false); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\t%c\t%s\n"), + (*n).first, + (*n).second.c_str ())); + } + + // Now run the same test in reverse. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("... in reverse ...\n"))); + + typedef Map::const_reverse_iterator const_reverse_iterator; + + letter = letters + letters_len - 1; + word = words + words_len - 1; + + // Work around compiler / STL implementations that cannot + // handle implicit conversions from iterator to const_iterator + // (e.g. due to missing template constructor.) + // + // We don't strictly need a const Map for this test but having + // one allows us to exercise const iterators. + Map const & const_phonetic = phonetic; + + const_reverse_iterator const rlast = const_phonetic.rend (); + for (const_reverse_iterator r = const_phonetic.rbegin (); + !(r == rlast); // Sun C++ Forte doesn't support operator!= + ++r, --letter, --word) + { + if ((*r).first != *letter || (*r).second != *word) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Key/Datum mismatch:\n") + ACE_TEXT (" key \"%c\" should be \"%c\"\n") + ACE_TEXT (" datum \"%s\" should be \"%s\"\n"), + (*r).first, + *letter, + (*r).second.c_str (), + word->c_str ()), + false); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\t%c\t%s\n"), + (*r).first, + (*r).second.c_str ())); + } + + // The size should not have changed. + ACE_ASSERT (phonetic.size () == letters_len); + + // Empty the map of its contents wholesale. + phonetic.clear (); + + ACE_ASSERT (phonetic.size () == 0 && phonetic.is_empty ()); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Index operator test passed.\n"))); + + return true; +} + +// -------------------------------------------------------------- + +class RefCounted +{ +public: + + RefCounted (void) + : refcount_ (0) + { + } + + RefCounted (unsigned int * count) + : refcount_ (count) + { + } + + ~RefCounted (void) + { + if (this->refcount_) + --(*this->refcount_); + } + + RefCounted (RefCounted const & r) + : refcount_ (r.refcount_ptr ()) + { + if (this->refcount_) + ++(*this->refcount_); + } + + RefCounted & + operator= (RefCounted const & r) + { + RefCounted tmp (r); + std::swap (this->refcount_, tmp.refcount_); + + return *this; + } + + unsigned int * + refcount_ptr (void) const + { + return this->refcount_; + } + + unsigned int + refcount (void) const + { + return *this->refcount_; + } + +private: + + unsigned int * refcount_; + +}; + +// -------- + +bool +reference_count_test (void) +{ + typedef ACE_Array_Map<ACE_TString, RefCounted> Map; + + static Map::size_type const CAPACITY = 30; + + unsigned int ref_count = 1; + + RefCounted counted (&ref_count); + + ACE_ASSERT (counted.refcount () == 1); + + { + Map map (CAPACITY); // Preallocate storage for a number of + // elements even if they are not used to test + // some internals. + + map[ACE_TEXT("One")] = counted; + + ACE_ASSERT (counted.refcount () == 2); + + + std::pair<Map::iterator, bool> result; + + { + // Enter a new scope block to assure destruction of temporaries + // on systems like Solaris / Sun C++. + + result = map.insert (std::make_pair (ACE_TString (ACE_TEXT ("Two")), + counted)); + + ACE_ASSERT (result.second); + } + + ACE_ASSERT (counted.refcount () == 3); + + { + // Enter a new scope block to assure destruction of temporaries + // on systems like Solaris / Sun C++. + + result = map.insert (std::make_pair (ACE_TString (ACE_TEXT ("Three")), + counted)); + + ACE_ASSERT (result.second); + } + + + ACE_ASSERT (counted.refcount () == 4); + + Map::size_type const erased = map.erase (ACE_TEXT ("One")); + + ACE_ASSERT (erased == 1); + ACE_ASSERT (counted.refcount () == 3); + } + + // Map instance no longer contains any references to the "counted" + // object so the reference count should be back to one. + + ACE_ASSERT (counted.refcount () == 1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Reference count test passed.\n"))); + + return true; +} + +// -------------------------------------------------------------- + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Array_Map_Test")); + + ACE_ASSERT (::letters_len == ::words_len); + + bool const success = + ::insertion_removal_test () + && ::index_operator_test () + && ::reference_count_test (); + + ACE_END_TEST; + + return (success ? 0 : -1); +} + diff --git a/ACE/tests/Atomic_Op_Test.cpp b/ACE/tests/Atomic_Op_Test.cpp new file mode 100644 index 00000000000..d807b287684 --- /dev/null +++ b/ACE/tests/Atomic_Op_Test.cpp @@ -0,0 +1,385 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Atomic_Op_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the Atomic Operations Class in ACE. +// On platforms like Win32, ACE uses template specialization to +// use native implementations provided by the OS to accelarate +// these operations. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "tests/test_config.h" + +ACE_RCSID(tests, Atomic_Op_Test, "$Id$") + +#include "ace/Atomic_Op.h" +#include "ace/Synch_Traits.h" + +enum { TEST_ITERATIONS = 1000000 }; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Atomic_Op_Test")); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, long> foo (5); + + ACE_ASSERT (foo == 5); + + long result = ++foo; + ACE_ASSERT (foo == 6); + ACE_ASSERT (result == 6); + + result = --foo; + ACE_ASSERT (foo == 5); + ACE_ASSERT (result == 5); + + result = foo++; + ACE_ASSERT (foo == 6); + ACE_ASSERT (result == 5); + + result = foo--; + ACE_ASSERT (foo == 5); + ACE_ASSERT (result == 6); + + result = foo += 10; + ACE_ASSERT (foo == 15); + ACE_ASSERT (result == 15); + + result = foo -= 10; + ACE_ASSERT (foo == 5); + ACE_ASSERT (result == 5); + + foo = 7; + ACE_ASSERT (foo == 7); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, long> foo2 (5); + foo2 = foo; + ACE_ASSERT (foo == 7); + ACE_ASSERT (foo2 == 7); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <long> assignment %D\n"))); + int i; + for (i = 0; i < TEST_ITERATIONS; ++i) + { + foo = 1; + foo = 2; + foo = 3; + foo = 4; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <long> assignment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <long> increment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + ++foo; + ++foo; + ++foo; + ++foo; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <long> increment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <long> decrement %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + --foo; + --foo; + --foo; + --foo; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <long> decrement %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <long> addition %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + foo += 5; + foo += 5; + foo += 5; + foo += 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <long> addition %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <long> subtraction %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + foo -= 5; + foo -= 5; + foo -= 5; + foo -= 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <long> subtraction %D\n"))); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, unsigned long> foo_unsigned (5); + + ACE_ASSERT (foo_unsigned == 5); + + unsigned long ul_result = ++foo_unsigned; + ACE_ASSERT (foo_unsigned == 6); + ACE_ASSERT (ul_result == 6); + + ul_result = --foo_unsigned; + ACE_ASSERT (foo_unsigned == 5); + ACE_ASSERT (ul_result == 5); + + ul_result = foo_unsigned++; + ACE_ASSERT (foo_unsigned == 6); + ACE_ASSERT (ul_result == 5); + + ul_result = foo_unsigned--; + ACE_ASSERT (foo_unsigned == 5); + ACE_ASSERT (ul_result == 6); + + ul_result = foo_unsigned += 10; + ACE_ASSERT (foo_unsigned == 15); + ACE_ASSERT (ul_result == 15); + + ul_result = foo_unsigned -= 10; + ACE_ASSERT (foo_unsigned == 5); + ACE_ASSERT (ul_result == 5); + + foo_unsigned = 7; + ACE_ASSERT (foo_unsigned == 7); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, unsigned long> foo_unsigned2 (5); + foo_unsigned2 = foo_unsigned; + ACE_ASSERT (foo_unsigned == 7); + ACE_ASSERT (foo_unsigned2 == 7); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned long> assignment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + foo_unsigned = 1; + foo_unsigned = 2; + foo_unsigned = 3; + foo_unsigned = 4; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned long> assignment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned long> increment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + ++foo_unsigned; + ++foo_unsigned; + ++foo_unsigned; + ++foo_unsigned; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned long> increment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned long> decrement %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + --foo_unsigned; + --foo_unsigned; + --foo_unsigned; + --foo_unsigned; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned long> decrement %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned long> addition %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + foo_unsigned += 5; + foo_unsigned += 5; + foo_unsigned += 5; + foo_unsigned += 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned long> addition %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned long> subtraction %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + foo_unsigned -= 5; + foo_unsigned -= 5; + foo_unsigned -= 5; + foo_unsigned -= 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned long> subtraction %D\n"))); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, int> bar (5); + + ACE_ASSERT (bar == 5); + + result = ++bar; + ACE_ASSERT (bar == 6); + ACE_ASSERT (result == 6); + + result = --bar; + ACE_ASSERT (bar == 5); + ACE_ASSERT (result == 5); + + result = bar++; + ACE_ASSERT (bar == 6); + ACE_ASSERT (result == 5); + + result = bar--; + ACE_ASSERT (bar == 5); + ACE_ASSERT (result == 6); + + result = bar += 10; + ACE_ASSERT (bar == 15); + ACE_ASSERT (result == 15); + + result = bar -= 10; + ACE_ASSERT (bar == 5); + ACE_ASSERT (result == 5); + + bar = 7L; + ACE_ASSERT (bar == 7); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, int> bar2 (5L); + bar2 = bar; + ACE_ASSERT (bar == 7); + ACE_ASSERT (bar2 == 7); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <int> assignment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + bar = 1; + bar = 2; + bar = 3; + bar = 4; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <int> assignment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <int> increment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + ++bar; + ++bar; + ++bar; + ++bar; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <int> increment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <int> decrement %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + --bar; + --bar; + --bar; + --bar; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <int> decrement %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <int> addition %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + bar += 5; + bar += 5; + bar += 5; + bar += 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <int> addition %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <int> subtraction %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + bar -= 5; + bar -= 5; + bar -= 5; + bar -= 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <int> subtraction %D\n"))); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, unsigned int> unsigned_bar (5); + + ACE_ASSERT (unsigned_bar == 5); + + unsigned int ui_result = ++unsigned_bar; + ACE_ASSERT (unsigned_bar == 6); + ACE_ASSERT (ui_result == 6); + + ui_result = --unsigned_bar; + ACE_ASSERT (unsigned_bar == 5); + ACE_ASSERT (ui_result == 5); + + ui_result = unsigned_bar++; + ACE_ASSERT (unsigned_bar == 6); + ACE_ASSERT (ui_result == 5); + + ui_result = unsigned_bar--; + ACE_ASSERT (unsigned_bar == 5); + ACE_ASSERT (ui_result == 6); + + ui_result = unsigned_bar += 10; + ACE_ASSERT (unsigned_bar == 15); + ACE_ASSERT (ui_result == 15); + + ui_result = unsigned_bar -= 10; + ACE_ASSERT (unsigned_bar == 5); + ACE_ASSERT (ui_result == 5); + + unsigned_bar = 7L; + ACE_ASSERT (unsigned_bar == 7); + + ACE_Atomic_Op <ACE_SYNCH_MUTEX, unsigned int> unsigned_bar2 (5L); + unsigned_bar2 = unsigned_bar; + ACE_ASSERT (unsigned_bar == 7); + ACE_ASSERT (unsigned_bar2 == 7); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned int> assignment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + unsigned_bar = 1; + unsigned_bar = 2; + unsigned_bar = 3; + unsigned_bar = 4; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned int> assignment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned int> increment %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + ++unsigned_bar; + ++unsigned_bar; + ++unsigned_bar; + ++unsigned_bar; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned int> increment %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned int> decrement %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + --unsigned_bar; + --unsigned_bar; + --unsigned_bar; + --unsigned_bar; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned int> decrement %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned int> addition %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + unsigned_bar += 5; + unsigned_bar += 5; + unsigned_bar += 5; + unsigned_bar += 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned int> addition %D\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting <unsigned int> subtraction %D\n"))); + for (i = 0; i < TEST_ITERATIONS; ++i) + { + unsigned_bar -= 5; + unsigned_bar -= 5; + unsigned_bar -= 5; + unsigned_bar -= 5; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ending <unsigned int> subtraction %D\n"))); + + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Auto_Event_Test.cpp b/ACE/tests/Auto_Event_Test.cpp new file mode 100644 index 00000000000..bc1f895746a --- /dev/null +++ b/ACE/tests/Auto_Event_Test.cpp @@ -0,0 +1,246 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Auto_Event Test +// +// = DESCRIPTION +// This test verifies the functionality of the <ACE_Auto_Event> +// implementation. +// +// = AUTHOR +// Martin Corino <mcorino@remedy.nl> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Auto_Event.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Auto_Event_Test, "$Id$") + +// msec that times are allowed to differ before test fails. +#if defined (ACE_HAS_HI_RES_TIMER) || defined (ACE_HAS_AIX_HI_RES_TIMER) || \ + defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER) || \ + defined (ACE_HAS_POWERPC_TIMER) +# define ACE_ALLOWED_SLACK 100 +#else /* don't have a high-res timer */ +# define ACE_ALLOWED_SLACK 1100 +#endif /* don't have a high-res timer */ + +// Test results, 'success' is 0 +static int test_result = 0; + +#if defined (ACE_HAS_THREADS) + +// Event used in the tests. Start it "unsignalled" (i.e., its initial +// state is 0). +static ACE_Auto_Event evt ((unsigned int) 0); + +// Default number of iterations. +static int n_iterations = 10; + +// Number of worker threads. +static size_t n_workers = 10; + +// Number of timeouts. +static size_t timeouts = 0; + +// Number of times to call test_timeout (). +static size_t test_timeout_count = 3; + +// Tests the amount of time spent in a timed wait. +static int +test_timeout (void) +{ + int status = 0; + + // milliseconds... + long msecs_expected; + long msecs_waited; + long msecs_diff; + + // Wait a little longer each time + static long wait_secs = 3; + + ACE_Time_Value wait = ACE_OS::gettimeofday (); + + ACE_Time_Value begin = wait; + + wait.sec (wait.sec () + wait_secs); + + if (evt.wait (&wait) == -1) + { + if (errno != ETIME) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test_timeout should be ETIME but is"))); + status = -1; + } + } + ACE_Time_Value wait_diff = ACE_OS::gettimeofday () - begin; + + msecs_waited = wait_diff.msec (); + msecs_expected = wait_secs * 1000; + msecs_diff = labs (msecs_expected - msecs_waited); + + if (msecs_diff > ACE_ALLOWED_SLACK) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Timed wait fails length test\n"))); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected %d ms, actual %d ms; %d allowed\n"), + (int)msecs_expected, + (int)msecs_waited, + (int)ACE_ALLOWED_SLACK)); + status = -1; + } + + ++wait_secs; + return status; +} + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-w n_workers] [-n iteration_count]\n"))); + ACE_OS::exit (1); +} + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("w:n:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'w': + n_workers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + n_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + print_usage_and_die (); + break; + } +} + +// Worker tries to acquire the semaphore, hold it for a while, and +// then releases it. + +static void * +worker (void *) +{ + for (int iterations = 1; + iterations <= n_iterations; + iterations++) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_Time_Value wait (0, + iterations * 1000 * 100); // Wait 'iter' msec + //FUZZ: enable check_for_lack_ACE_OS + + ACE_Time_Value tv = ACE_OS::gettimeofday () + wait; + if (evt.wait (&tv) == -1) + { + // verify that we have ETIME + if (ACE_OS::last_error() != ETIME) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Worker should be ETIME but is"))); + } + else + ++timeouts; + ACE_Time_Value diff = ACE_OS::gettimeofday (); + diff = diff - tv; // tv should have been reset to time acquired + long diff_msec = diff.msec (); + + if (diff_msec > ACE_ALLOWED_SLACK) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Acquire fails time reset test\n"))); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Diff btw now and returned time: %d ms; ") + ACE_TEXT ("%d allowed\n"), + (int)diff_msec, + (int)ACE_ALLOWED_SLACK)); + test_result = 1; + } + // Hold the lock for a while. + ACE_OS::sleep (ACE_Time_Value (0, + (ACE_OS::rand () % 1000) * 1000)); + evt.signal (); + } + + ACE_Thread::yield (); + } + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Test event functionality. + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Auto_Event_Test")); + +#if defined (ACE_HAS_THREADS) + parse_args (argc, argv); + ACE_OS::srand ((u_int) ACE_OS::time (0L)); + + //Test timed waits. + for (size_t i = 0; i < test_timeout_count; i++) + if (test_timeout () != 0) + test_result = 1; + + if (ACE_Thread_Manager::instance ()->spawn_n + (static_cast<size_t> (n_workers), + ACE_THR_FUNC (worker), + 0, + THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), + 1); + + // Release the first worker. + evt.signal (); + + ACE_Thread_Manager::instance ()->wait (); + + size_t percent = (timeouts * 100) / (n_workers * n_iterations); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Worker threads timed out %d percent of the time\n"), + (int)percent)); + + if (test_result == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Auto_Event Test successful\n"))); +#else + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return test_result; +} diff --git a/ACE/tests/Auto_IncDec_Test.cpp b/ACE/tests/Auto_IncDec_Test.cpp new file mode 100644 index 00000000000..3308a35445b --- /dev/null +++ b/ACE/tests/Auto_IncDec_Test.cpp @@ -0,0 +1,141 @@ +// $Id$ + +//============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Auto_IncDec_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the Auto Increment/Decrement Class in +// ACE. +// +// = AUTHOR +// Edan Ayal <EdanA@cti2.com> +// +//============================================================================ + +#include "tests/test_config.h" +#include "ace/Auto_IncDec_T.h" +#include "ace/Thread_Manager.h" +#include "ace/Atomic_Op.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Auto_IncDec_Test, "Auto_IncDec_Test.cpp, by Edan Ayal") + +#if defined (ACE_HAS_THREADS) + +// Default number of threads. +static size_t n_threads = 15; + +typedef ACE_Atomic_Op<ACE_Thread_Mutex, int> INTERLOCKED_INT; +static INTERLOCKED_INT current_threads_in_first_section; +static INTERLOCKED_INT current_threads_in_second_section; + +static void * +worker (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) worker starting\n"))); + + { // First section. + ACE_Auto_IncDec<INTERLOCKED_INT> threads_in_section_auto_inc_dec + (current_threads_in_first_section); + + // Wait according to the number of threads... + ACE_Time_Value pause (current_threads_in_first_section.value (), + 0); + ACE_OS::sleep (pause); + } + + { // Second section. + ACE_Auto_IncDec<INTERLOCKED_INT> threads_in_section_auto_inc_dec + (current_threads_in_second_section); + + // Wait according to the number of threads inside the previous + // section... + ACE_Time_Value pause (current_threads_in_first_section.value (), + 0); + ACE_OS::sleep (pause); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) worker exiting\n"))); + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Spawn off threads. + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Auto_IncDec_Test")); + +#if defined (ACE_HAS_THREADS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) main thread starting\n"))); + + current_threads_in_first_section = 0; + current_threads_in_second_section = 0; + + if (ACE_Thread_Manager::instance ()->spawn_n + (n_threads, + ACE_THR_FUNC (worker), + 0, + THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), + -1); + // Make sure at least one thread is started... + ACE_Thread::yield (); + + while (ACE_Thread_Manager::instance ()->count_threads ()) + { + // wait according to the number of threads... + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" %d in first section, %d in second section, %d total\n"), + current_threads_in_first_section.value (), + current_threads_in_second_section.value (), + ACE_Thread_Manager::instance ()->count_threads ())); + + ACE_Time_Value pause (1, 0); + ACE_OS::sleep (pause); + } + + ACE_Thread_Manager::instance ()->wait (); + + ACE_ASSERT (current_threads_in_first_section.value () == 0 + && current_threads_in_second_section.value () == 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) exiting main thread\n"))); +#else + int counter = 0; + { + ACE_Auto_IncDec<int> Auto_IncDec1 (counter); + ACE_ASSERT (counter == 1); + + ACE_Auto_IncDec<int> Auto_IncDec2 (counter); + ACE_ASSERT (counter == 2); + + { + ACE_ASSERT (counter == 2); + ACE_Auto_IncDec<int> Auto_IncDec3 (counter); + ACE_ASSERT (counter == 3); + } + + ACE_ASSERT (counter == 2); + } + + ACE_ASSERT (counter == 0); + +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Barrier_Test.cpp b/ACE/tests/Barrier_Test.cpp new file mode 100644 index 00000000000..9f0bf8ecdd8 --- /dev/null +++ b/ACE/tests/Barrier_Test.cpp @@ -0,0 +1,138 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Barrier_Test.cpp +// +// = DESCRIPTION +// This program illustrates how the ACE barrier synchronization +// mechanisms work. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Doug Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Barrier.h" +#include "ace/Thread_Manager.h" + +ACE_RCSID(tests, Barrier_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +struct Tester_Args + // = TITLE + // These arguments are passed into each test thread. +{ + Tester_Args (ACE_Barrier &tb, int i) + : tester_barrier_ (tb), + n_iterations_ (i) {} + + ACE_Barrier &tester_barrier_; + // Reference to the tester barrier. This controls each iteration + // of the tester function running in every thread. + + int n_iterations_; + // Number of iterations to run. +}; + +// Iterate <n_iterations> time printing off a message and "waiting" +// for all other threads to complete this iteration. + +static void * +wait_tester (Tester_Args *args) +{ + for (int iterations = 1; + iterations <= args->n_iterations_; + iterations++) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) in iteration %d\n"), + iterations)); + + // Block until all other threads have waited, then continue. + if (args->tester_barrier_.wait () != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("wait failed"))); + } + + return 0; +} + +// Wait on the barrier, expecting it to be shut down before completing +// the wait. + +static void * +shut_tester (Tester_Args *args) +{ + if (args->tester_barrier_.wait () == 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) wait succeeded, should have shut down\n"))); + else if (errno != ESHUTDOWN) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) wait failed, expecting ESHUTDOWN, %p\n"), + ACE_TEXT ("got"))); + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Barrier_Test")); + +#if defined (ACE_HAS_THREADS) + int n_threads = ACE_MAX_THREADS; + int n_iterations = ACE_MAX_ITERATIONS; + + ACE_Barrier tester_barrier (n_threads); + + Tester_Args args (tester_barrier, n_iterations); + + for (size_t iteration_count = 0; + iteration_count < ACE_MAX_ITERATIONS; + iteration_count++) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("starting iteration %d\n"), + iteration_count)); + + if (ACE_Thread_Manager::instance ()->spawn_n + (n_threads, + (ACE_THR_FUNC) wait_tester, + (void *) &args, + THR_NEW_LWP | THR_JOINABLE) == -1) + + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), 1); + + ACE_Thread_Manager::instance ()->wait (); + } + + // Now test ACE_Barrier shutdown. Set up a barrier for n_threads, and start + // n_threads - 1 threads to wait, then shut the barrier down. + ACE_Barrier shut_barrier (n_threads); + Tester_Args shut_args (shut_barrier, 1); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Starting shutdown test threads\n"))); + if (ACE_Thread_Manager::instance ()->spawn_n + (n_threads - 1, + (ACE_THR_FUNC) shut_tester, + (void *) &shut_args, + THR_NEW_LWP | THR_JOINABLE) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n")), 1); + + shut_barrier.shutdown (); + ACE_Thread_Manager::instance ()->wait (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test done\n"))); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Based_Pointer_Test.cpp b/ACE/tests/Based_Pointer_Test.cpp new file mode 100644 index 00000000000..41d548d2039 --- /dev/null +++ b/ACE/tests/Based_Pointer_Test.cpp @@ -0,0 +1,427 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Based_Pointer_Test.cpp +// +// = DESCRIPTION +// This test check the Based_Pointer and Based_Pointer_repository classes. +// +// = AUTHOR +// Steve Williams <steve@telxio> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/DLL.h" +#include "ace/ACE.h" +#include "ace/OS.h" +#ifdef ACE_HAS_POSITION_INDEPENDENT_POINTERS +#include "ace/Based_Pointer_Repository.h" +#endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS */ +#include "ace/Malloc_T.h" +#include "ace/MMAP_Memory_Pool.h" +#include "ace/PI_Malloc.h" +#include "ace/Null_Mutex.h" + +ACE_RCSID (tests, + Based_Pointer_Repository_Test, + "$Id$") + +#ifdef ACE_HAS_POSITION_INDEPENDENT_POINTERS + +#if defined (ACE_WIN32) && defined (_MSC_VER) && defined (_DEBUG) +# define OBJ_SUFFIX ACE_TEXT ("d") ACE_DLL_SUFFIX +#elif defined (ACE_WIN32) && defined (__BORLANDC__) +# define OBJ_SUFFIX ACE_LD_DECORATOR_STR ACE_DLL_SUFFIX +#else +# define OBJ_SUFFIX ACE_DLL_SUFFIX +#endif /* ACE_WIN32 && && _MSC_VER && _DEBUG */ + +#if defined (ACE_WIN32) || defined (ACE_OPENVMS) +# define OBJ_PREFIX ACE_DLL_PREFIX +#else +# define OBJ_PREFIX ACE_TEXT("./") ACE_DLL_PREFIX +#endif /* ACE_WIN32 */ + +// Declare the type of the DLL symbol: +typedef void *(*Get_Bp_Repository_Inst)(void); + +// Declare an allocator based MMAP_Memory_Pool +typedef ACE_Malloc_T< ACE_MMAP_MEMORY_POOL, ACE_Null_Mutex, ACE_PI_Control_Block + > MMAP_Allocator; + + +// Check that the ACE_Based_Pointer_Repository can be accessed +// from a Windows DLL +// (see http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=1991) +int singleton_test (void) +{ + void* baddr1 = ACE_BASED_POINTER_REPOSITORY::instance(); + void* baddr2 = ACE_BASED_POINTER_REPOSITORY::instance(); + + if (baddr1 != baddr2) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("ACE_Based_Pointer_Repository is not a singleton\n")), + -1); + } + +// Protection against this test being run on platforms not supporting Dlls. +#if defined(ACE_HAS_DYNAMIC_LINKING) + + ACE_DLL dll; + + // If DLL causes multiple instances of singleton + // then the ACE_Cleanup object registered + // with the ACE_Object_manager will no longer be valid, + // at exit time if the library is unloaded. Override + // the default close on destruct. + int retval = dll.open (OBJ_PREFIX + ACE_TEXT ("Based_Pointer_Test_Lib") + OBJ_SUFFIX, + ACE_DEFAULT_SHLIB_MODE, + 0); + + if (retval != 0) + { + ACE_TCHAR *dll_error = dll.error (); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error in DLL Open: %s\n"), + dll_error ? dll_error : ACE_TEXT ("unknown error")), + -1); + } + +#if defined (ACE_OPENVMS) + // with OPENVMS symbol names > 31 cause us trouble with dlsym() + void* foo = dll.symbol (ACE_TEXT ("get_based_pointer_repo_inst")); +#else + void* foo = dll.symbol (ACE_TEXT ("get_based_pointer_repository_instance")); +#endif + + // Cast the void* to function* with a long as intermediate. + ptrdiff_t tmp = reinterpret_cast<ptrdiff_t> (foo); + Get_Bp_Repository_Inst get_bp_repository_inst = + reinterpret_cast<Get_Bp_Repository_Inst> (tmp); + if (get_bp_repository_inst == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + dll.error ()), + -1); + + void* baddr_dll = get_bp_repository_inst (); + + dll.close (); + + if (baddr_dll != baddr1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("ACE_Based_Pointer_Repository is not a ") + ACE_TEXT ("singleton in DLL %@ %@\n"), + baddr_dll, + baddr1), + -1); + } +#endif /* ACE_HAS_DYNAMIC_LINKING */ + + return 0; +} + +// Check that MMAP memory blocks are correctly mapped +// into the Based_Pointer_Repository +int +mmap_map_test(void) +{ + MMAP_Allocator* alloc = 0; + + ACE_OS::unlink("foo"); + { + // The 'options' are only here to quiet MSVC 6. It can be removed + // when MSVC 6 support is removed. + MMAP_Allocator::MEMORY_POOL_OPTIONS *options = 0; + ACE_NEW_RETURN + (alloc, + MMAP_Allocator (ACE_TEXT ("foo"), ACE_TEXT ("foo"), options), + -1); + + void* addr = alloc->base_addr(); + if(addr == 0) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT ("Unable to get base to MMAP Memory Pool\n"))); + alloc->remove(); + delete alloc; + return -1; + } + + // Check a base address mapping was added to the Repository + // when the pool was created + void* ba = 0; + if(ACE_BASED_POINTER_REPOSITORY::instance()->find(addr, ba) == -1) + { + ACE_ERROR((LM_ERROR, ACE_TEXT ("Unable to access repository\n"))); + alloc->remove(); + delete alloc; + return -1; + } + + alloc->remove(); + delete alloc; + + if(ba != addr) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MMAP pool mapping not present\n")), + -1); + } + + // Check Mapping is removed when object is deleted + if (ACE_BASED_POINTER_REPOSITORY::instance()->find(addr, ba) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to access repository\n")), + -1); + } + if(ba != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MMAP pool mapping not removed\n")), + -1); + } + } + return 0; +} + +// Check that persistent MMAP memory blocks are correctly remapped +// into the Based_Pointer_Repository +// (i.e. maps based on backing stores that are already +// present in the filesystem) +// (see http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2216) +int +mmap_persistent_map_test(void) +{ + MMAP_Allocator* alloc = 0; + + // The 'options' are only here to quiet MSVC 6. It can be removed + // when MSVC 6 support is removed. + MMAP_Allocator::MEMORY_POOL_OPTIONS *options = 0; + ACE_OS::unlink("foo"); + { + ACE_NEW_RETURN + (alloc, + MMAP_Allocator (ACE_TEXT ("foo"), ACE_TEXT ("foo"), options), + -1); + alloc->sync(); + + // Delete Malloc and the memory pool, but do not remove + // the backing store + alloc->memory_pool().release(0); + delete alloc; + } + // + // Recreate segment with existing backing store + // + ACE_NEW_RETURN + (alloc, + MMAP_Allocator (ACE_TEXT ("foo"), ACE_TEXT("foo"), options), + -1); + + void* addr = alloc->base_addr(); + if(addr == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unable to get base to persistent MMAP Memory Pool\n"))); + alloc->remove(); + delete alloc; + return -1; + } + void* ba = 0; + if(ACE_BASED_POINTER_REPOSITORY::instance()->find(addr, ba) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unable to find base address after map of persistent segment\n"))); + alloc->remove(); + delete alloc; + return -1; + } + if(ba == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Persistent MMAP Memory Pool not mapped\n"))); + alloc->remove(); + delete alloc; + return -1; + } + + alloc->remove(); + delete alloc; + return 0; +} + +// Check that MMAP memory blocks are correctly remapped +// into the Based_Pointer_Repository +// (i.e. when a segment is resized it may move its base address +// because the OS cannot fit the new segment size at the same +// base address, in this case the Repository must be updated) +// (see http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2218) +int +mmap_remap_test(void) +{ + // Use a Position Independent memory segment + // because this one is going to move + + MMAP_Allocator *alloc[ 3 ]= {0, 0, 0}; + void *pool_base[ 3 ]= {0, 0, 0}; + + // Make sure the Pool options are set to allow + // the segment to move + ACE_MMAP_Memory_Pool_Options data_opts( + 0, + ACE_MMAP_Memory_Pool_Options::NEVER_FIXED ); + int i; + + for (i= 0; i<3; ++i) + { + ACE_TCHAR store[ MAXPATHLEN + 1 ]; + ACE_OS::sprintf( store, ACE_TEXT("foo%d"), i ); + ACE_OS::unlink( store ); + + ACE_NEW_RETURN (alloc[ i ], + MMAP_Allocator (store, store, &data_opts), + -1); + pool_base[ i ]= alloc[ i ]->base_addr(); + } + + // sort pools into base address order + for (i= 0; i<2; ++i) + { + if (pool_base[ i ] < pool_base[ i+1 ]) + { + void *tmp1= pool_base[ i ]; + MMAP_Allocator *tmp2= alloc[ i ]; + pool_base[ i ]= pool_base[ i+1 ]; + alloc[ i ]= alloc[ i+1 ]; + pool_base[ i+1 ]= tmp1; + alloc[ i+1 ]= tmp2; + i= -1; + } + } + + // alloc[1] is now bounded, whether memory grows up or + // down, it will hit either alloc[0] or alloc[2] and have + // to be remapped. + // + // Calculate maximum space between base addresses + + size_t size= (char *) pool_base[ 0 ] - (char *) pool_base[ 1 ]; + size_t tmpsize= (char *) pool_base[ 1 ] - (char *) pool_base[ 2 ]; + size= (size < tmpsize) ? tmpsize : size; + + // force pool to move + ++size; + + (void)alloc[ 1 ]->malloc(size); + void *nba= alloc[ 1 ]->base_addr(); + + if (pool_base[ 1 ] == nba) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("MMAP Pool did not move base address as expected\n"))); + for (i= 0; i<3; ++i) + { + alloc[ i ]->remove(); + delete alloc[ i ]; + } + return -1; + } + + void *ba= 0; + if (ACE_BASED_POINTER_REPOSITORY::instance()->find(nba, ba) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unable to find base address after remap of segment\n"))); + for (i= 0; i<3; ++i) + { + alloc[ i ]->remove(); + delete alloc[ i ]; + } + return -1; + } + + if (ba != nba) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("New base address not mapped after MMAP remap\n"))); + for (i= 0; i<3; ++i) + { + alloc[ i ]->remove(); + delete alloc[ i ]; + } + return -1; + } + + // Check old base address has been removed + // from the repository + if (ACE_BASED_POINTER_REPOSITORY::instance()->find( pool_base[ 1 ], ba ) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unable to find base address after remap of segment\n"))); + for (i= 0; i<3; ++i) + { + alloc[ i ]->remove(); + delete alloc[ i ]; + } + return -1; + } + + if (ba == pool_base[ 1 ]) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Old base address not removed after MMAP remap\n"))); + for (i= 0; i<3; ++i) + { + alloc[ i ]->remove(); + delete alloc[ i ]; + } + return -1; + } + + for (i= 0; i<3; ++i) + { + alloc[ i ]->remove(); + delete alloc[ i ]; + } + return 0; +} + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Based_Pointer_Test")); + + int retval = 0; + + retval += singleton_test (); + retval += mmap_map_test(); + retval += mmap_persistent_map_test(); + retval += mmap_remap_test(); + + ACE_END_TEST; + return retval == 0 ? 0 : 1; +} + +#else /* ! ACE_HAS_POSITION_INDEPENDENT_POINTERS */ +// Nothing to test ! +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Based_Pointer_Test")); + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS */ diff --git a/ACE/tests/Based_Pointer_Test_Lib.cpp b/ACE/tests/Based_Pointer_Test_Lib.cpp new file mode 100644 index 00000000000..e7047b70fa3 --- /dev/null +++ b/ACE/tests/Based_Pointer_Test_Lib.cpp @@ -0,0 +1,39 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Based_Pointer_Test_Lib.cpp +// +// = DESCRIPTION +// This test confirms the function of the Based_Pointer_Repository +// +// = AUTHOR +// Steve Williams <steve@telxio> +// +// ============================================================================ + +#include "ace/ACE.h" +#include "ace/svc_export.h" +#include "ace/Based_Pointer_Repository.h" + +ACE_RCSID (tests, + Based_Pointer_Repository_DLL_Test, + "$Id$") + +#if defined (ACE_OPENVMS) + // with OPENVMS symbol names > 31 cause us trouble with dlsym() +extern "C" ACE_Svc_Export void * +get_based_pointer_repo_inst (void) +#else +extern "C" ACE_Svc_Export void * +get_based_pointer_repository_instance (void) +#endif +{ + void* baddr = ACE_BASED_POINTER_REPOSITORY::instance(); + return baddr; +} + diff --git a/ACE/tests/Basic_Types_Test.cpp b/ACE/tests/Basic_Types_Test.cpp new file mode 100644 index 00000000000..7fd5afb584b --- /dev/null +++ b/ACE/tests/Basic_Types_Test.cpp @@ -0,0 +1,319 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Basic_Types_Test.cpp +// +// = DESCRIPTION +// Checks the #defines in ace/Basic_Types.h, and a few other basics. +// +// = AUTHOR +// David L. Levine <levine@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/config-all.h" +// Don't use the ACE version accessors in class ACE, so that we can +// support this test cleanly with the OS component, only. +#include "ace/Version.h" + +#if defined (ACE_HAS_MINIMAL_ACE_OS) + // Redefine these macros to allow the test to print out useful info. +# undef ACE_DEBUG +# define ACE_DEBUG(x) ACE_OS::fprintf x +# define LM_DEBUG stdout +# undef ACE_ERROR +# define ACE_ERROR(x) ACE_OS::fprintf x +# define LM_ERROR stderr +# define ACE_START_TEST(x) ACE_OS::printf (x ACE_TEXT ("\n")) +# define ACE_END_TEST +#else /* ! ACE_HAS_MINIMAL_ACE_OS */ +# include "test_config.h" +#endif /* ! ACE_HAS_MINIMAL_ACE_OS */ + +#include "ace/Basic_Types.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Basic_Types_Test, "$Id$") + +typedef void* (*a_function_pointer) (void*); + +static +u_int +check (const ACE_TCHAR *message, u_int i, u_int j) +{ + if (i == j) + { + ACE_DEBUG ((LM_DEBUG, message, j, ACE_TEXT ("\n"))); + return 0; + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("assertion failed \"%s\": %u != %u\n"), + message, i, j)); + return 1; + } +} + +// If the platform lacks an unsigned long long, define one. +#if defined (ACE_LACKS_LONGLONG_T) || defined (ACE_LACKS_UNSIGNEDLONGLONG_T) +static +u_int +check_ace_u_longlong (const ACE_TCHAR *const name, + const ACE_U_LongLong ull, + const u_long hi, + const u_long lo) +{ + if (ull.hi () == hi && ull.lo () == lo) + return 0; + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s; hi: %x, should be %x; ") + ACE_TEXT ("lo: %x, should be %x.\n"), + name, ull.hi (), hi, ull.lo (), lo), + 1); +} + +static +u_int +test_ace_u_longlong (void) +{ + u_int errors = 0; + + ACE_U_LongLong ull1 (0x21,1); + errors += check_ace_u_longlong (ACE_TEXT ("ull1"), ull1, 1, 0x21); + + ACE_U_LongLong ull2 (0x20,2); + errors += check_ace_u_longlong (ACE_TEXT ("ull2"), ull2, 2, 0x20); + + ull2 -= ull1; + errors += check_ace_u_longlong (ACE_TEXT ("ull2"), ull2, 0, 0xfffffffful); + + ull2 += ull1; + errors += check_ace_u_longlong (ACE_TEXT ("ull2"), ull2, 2, 0x20); + + ACE_U_LongLong ull3 = ull1 + ull1; + errors += check_ace_u_longlong (ACE_TEXT ("ull3"), ull3, 2, 0x42); + + ACE_U_LongLong ull4 = ACE_U_LongLong (0x1111, 0) - + ACE_U_LongLong (0x1112, 0); + errors += check_ace_u_longlong (ACE_TEXT ("ull4"), ull4, 0xfffffffful, 0xfffffffful); + + ACE_U_LongLong ull5 = ACE_U_LongLong (0x1111, 1) - + ACE_U_LongLong (0x1112, 0); + errors += check_ace_u_longlong (ACE_TEXT ("ull5"), ull5, 0, 0xfffffffful); + + ++ull5; + errors += check_ace_u_longlong (ACE_TEXT ("ull5"), ull5, 1, 0); + + ACE_U_LongLong ull6 = ull2 + ACE_U_LongLong (0, 1); + errors += check_ace_u_longlong (ACE_TEXT ("ull6"), ull6, 3, 0x20); + + ull6 += ACE_U_LongLong (0xffffffff, 0xfff0); + errors += check_ace_u_longlong (ACE_TEXT ("ull6"), ull6, 0xfff4, 0x1f); + + ++ull6; + errors += check_ace_u_longlong (ACE_TEXT ("ull6"), ull6, 0xfff4, 0x20); + + // The hi part of ull6 will be lost in the following, because + // the quotient has only 32 bits. + errors += check_ace_u_longlong (ACE_TEXT ("ull6 / 1"), + (ACE_U_LongLong) (ull6 / 1u), + 0, 0x20); + + // There's apparently a small loss in precision in + // ACE_U_LongLong::operator/. It calculates + // ull6 / 0xd0000 as 0x13b013b4 instead of 0x13b04ec4. + errors += check_ace_u_longlong (ACE_TEXT ("ull6 / 0x10000 / 0xd"), + (ACE_U_LongLong) (ull6 / 0x10000u / 0xd), + 0, 0x13b04ec4); + + errors += check_ace_u_longlong (ACE_TEXT ("ull6 % 5"), + (ACE_U_LongLong) (ull6 % 5), + 0, 1); + + errors += check_ace_u_longlong (ACE_TEXT ("ull6 % 0x20007"), + (ACE_U_LongLong) (ull6 % 0x20007), + 0, 0x3f63); + + ACE_U_LongLong ull7 (12); + ull7 *= 3125; + errors += check_ace_u_longlong (ACE_TEXT ("12 * 3125"), + ull7, + 0, 37500); + + ull7 *= 100; + errors += check_ace_u_longlong (ACE_TEXT ("37500 * 100"), + ull7, + 0, 3750000); + + errors += check_ace_u_longlong (ACE_TEXT ("3750000 << 16"), + ull7 << 16 , + 0x39, 0x38700000); + + errors += check_ace_u_longlong (ACE_TEXT ("3750000 >> 16"), + ull7 >> 16, + 0, 0x39); + + ull7 <<= 32; + errors += check_ace_u_longlong (ACE_TEXT ("3750000 <<= 32"), + ull7, + 3750000, 0); + + ull7 >>= 12; + errors += check_ace_u_longlong (ACE_TEXT ("3750000 <<= 32 >>= 15"), + ull7, + 0x393, 0x87000000); + + ACE_U_LongLong ull8 (0x0f0f, 0xf0f0); + ACE_U_LongLong ull9 (0xf0f0, 0xf0f0); + ull8 |= ull9; + errors += check_ace_u_longlong (ACE_TEXT ("ull8 |= ull9"), + ull8, + 0xf0f0, 0xffff); + + ull9.lo (0x5678); + ull9.hi (0x1234); + ull8 &= ull9; + errors += check_ace_u_longlong (ACE_TEXT ("ull8 &= 0x12345678"), + ull9, + 0x1234, 0x5678); + + return errors; +} + +#else +static u_int +test_ace_u_longlong (void) +{ + return 0; /* Platform has native types; no need to test ACE_U_LongLong */ +} +#endif /* ACE_LACKS_LONGLONG_T || ACE_LACKS_UNSIGNEDLONGLONG_T */ + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Basic_Types_Test")); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("This is ACE Version %u.%u.%u\n\n"), + ACE_MAJOR_VERSION, + ACE_MINOR_VERSION, + ACE_BETA_VERSION)); + + u_int errors = 0; + + errors += check (ACE_TEXT ("ACE_SIZEOF_CHAR: %u%s"), + sizeof (char), ACE_SIZEOF_CHAR); +#if defined (ACE_HAS_WCHAR) + errors += check (ACE_TEXT ("ACE_SIZEOF_WCHAR: %u%s"), + sizeof (wchar_t), ACE_SIZEOF_WCHAR); +#endif /* ACE_HAS_WCHAR */ + errors += check (ACE_TEXT ("ACE_SIZEOF_SHORT: %u%s"), + sizeof (short), ACE_SIZEOF_SHORT); + errors += check (ACE_TEXT ("ACE_SIZEOF_INT: %u%s"), + sizeof (int), ACE_SIZEOF_INT); + errors += check (ACE_TEXT ("ACE_SIZEOF_LONG: %u%s"), + sizeof (long), ACE_SIZEOF_LONG); + errors += check (ACE_TEXT ("ACE_SIZEOF_LONG_LONG: %u%s"), +#if defined (ACE_LACKS_LONGLONG_T) + sizeof (ACE_U_LongLong), +#elif defined (ACE_WIN32) + sizeof (unsigned __int64), +#else /* ! ACE_WIN32 && ! ACE_LACKS_LONGLONG_T */ + sizeof (long long), +#endif /* ! ACE_WIN32 && ! ACE_LACKS_LONGLONG_T */ + ACE_SIZEOF_LONG_LONG); + errors += check (ACE_TEXT ("sizeof 64-bit literal: %u%s"), + sizeof ACE_UINT64_LITERAL (1), + 8); + errors += check (ACE_TEXT ("ACE_SIZEOF_VOID_P: %u%s"), + sizeof (void *), ACE_SIZEOF_VOID_P); + errors += check (ACE_TEXT ("ACE_SIZEOF_FLOAT: %u%s"), + sizeof (float), ACE_SIZEOF_FLOAT); + errors += check (ACE_TEXT ("ACE_SIZEOF_DOUBLE: %u%s"), + sizeof (double), ACE_SIZEOF_DOUBLE); + errors += check (ACE_TEXT ("ACE_SIZEOF_LONG_DOUBLE: %u%s"), + sizeof (long double), ACE_SIZEOF_LONG_DOUBLE); + +// Crays don't have 16-bit quantities, so don't even test for 16-bit values +#if !defined(_UNICOS) + errors += check (ACE_TEXT ("sizeof (ACE_INT16) is %u%s"), + sizeof (ACE_INT16), 2); + errors += check (ACE_TEXT ("sizeof (ACE_UINT16) is %u%s"), + sizeof (ACE_INT16), 2); +#else /* ! _UNICOS */ + errors += check (ACE_TEXT ("sizeof (ACE_INT16) is %u%s"), + sizeof (ACE_INT16), 8); + errors += check (ACE_TEXT ("sizeof (ACE_UINT16) is %u%s"), + sizeof (ACE_INT16), 8); +#endif /* ! _UNICOS */ + +// MPP Crays do have 32-bit quantities (short), though vector Crays don't +#if !defined(_UNICOS) || defined(_CRAYMPP) + errors += check (ACE_TEXT ("sizeof (ACE_INT32) is %u%s"), + sizeof (ACE_INT32), 4); + errors += check (ACE_TEXT ("sizeof (ACE_UINT32) is %u%s"), + sizeof (ACE_INT32), 4); +#else /* ! _UNICOS */ + errors += check (ACE_TEXT ("sizeof (ACE_INT32) is %u%s"), + sizeof (ACE_INT32), 8); + errors += check (ACE_TEXT ("sizeof (ACE_UINT32) is %u%s"), + sizeof (ACE_INT32), 8); +#endif /* ! _UNICOS */ + errors += check (ACE_TEXT ("sizeof (ACE_UINT64) is %u%s"), + sizeof (ACE_UINT64), 8); + + errors += test_ace_u_longlong (); + + // ACE assumes sizeof (ptrdiff_t) == sizeof (void*) + if (sizeof (ptrdiff_t) == sizeof (void *)) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("sizeof (ptrdiff_t) == sizeof (void*)\n"))); + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("sizeof (ptrdiff_t) != sizeof (void*)\n"))); + ++errors; + } + + // ACE assumes sizeof (ptrdiff_t) >= sizeof (a_function_pointer) + if (sizeof (ptrdiff_t) >= sizeof (a_function_pointer)) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("sizeof (ptrdiff_t) >= sizeof (a_function_pointer)\n"))); + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("sizeof (ptrdiff_t) < sizeof (a_function_pointer)\n"))); + ++errors; + } + +#if defined (ACE_LITTLE_ENDIAN) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("little endian\n"))); +#elif defined (ACE_BIG_ENDIAN) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("big endian\n"))); +#else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("assertion failed: no ACE_*_ENDIAN definition!\n"))); + ++errors; +#endif /* ACE_LITTLE_ENDIAN */ + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("OS page size: %u\n"), + ACE_OS::getpagesize ())); + +#if defined (_SC_PAGESIZE) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("sysconf page size: %d\n"), + (int) ACE_OS::sysconf (_SC_PAGESIZE))); +#endif /* _SC_PAGESIZE */ +#if defined (_SC_CLK_TCK) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("clock ticks/sec = %d\n"), + (int) ACE_OS::sysconf (_SC_CLK_TCK))); +#endif /* _SC_CLK_TCK */ + + + ACE_END_TEST; + return errors == 0 ? 0 : 1; +} diff --git a/ACE/tests/Bound_Ptr_Test.cpp b/ACE/tests/Bound_Ptr_Test.cpp new file mode 100644 index 00000000000..f82cb2b30b7 --- /dev/null +++ b/ACE/tests/Bound_Ptr_Test.cpp @@ -0,0 +1,467 @@ +// $Id$ + +//============================================================================= +/** + * @file Bound_Ptr_Test.cpp + * + * $Id$ + * + * This example tests the <ACE_Strong_Bound_Ptr> and + * <ACE_Weak_Bound_Ptr> and illustrates how they may be dispersed + * between multiple threads using an implementation of the Active + * Object pattern, which is available in the POSA2 book + * <http://www.cs.wustl.edu/~schmidt/POSA>. + * + * @author Christopher Kohlhoff <chris@kohlhoff.com> + */ +//============================================================================= + +#include "test_config.h" +#include "ace/Null_Mutex.h" +#include "ace/Method_Request.h" +#include "Bound_Ptr_Test.h" + +ACE_RCSID (tests, Bound_Ptr_Test, "Bound_Ptr_Test.cpp,v 4.8 2000/04/23 04:43:58 brunsch Exp") + +// The following Parent and Child classes illustrate how you might use the +// ACE_Strong_Bound_Ptr and ACE_Weak_Bound_Ptr together in cyclic +// relationships. + +struct Child_Base +{ + virtual ~Child_Base (void); + + // Perform some operation. + virtual void do_something (void) = 0; +}; + + +// This class should only be created on the heap. Normally it would be an +// abstract class, and the implementation would be elsewhere. +struct Parent +{ + Parent (void); + ~Parent (void); + + // Weak pointer to this object used to hand out new references. Must be + // weak since it can't own itself! + ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> weak_self_; + + // The parent owns the child. When the parent is destroyed the child will + // be automatically deleted. + ACE_Strong_Bound_Ptr<Child_Base, ACE_Null_Mutex> child_; + + // Called by the child to perform some operation. + void do_something (void); + + static size_t instance_count_; +}; + +// This class should only be created on the heap. Normally it would be an +// abstract class, and the implementation would be elsewhere. +struct Child : public Child_Base +{ + Child (ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> parent); + virtual ~Child (void); + + // Back pointer to the parent. The child does not own the parent so has no + // effect on its lifetime. + ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> parent_; + + // Perform some operation. Delegates the work to the parent. + virtual void do_something (void); + + static size_t instance_count_; +}; + +Child_Base::~Child_Base (void) +{ +} + + +size_t Parent::instance_count_ = 0; + +Parent::Parent (void) + : weak_self_(this), + child_(new Child(weak_self_)) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Creating Parent object\n"))); + ++Parent::instance_count_; +} + +Parent::~Parent (void) +{ + --Parent::instance_count_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Deleting Parent object\n"))); +} + +void Parent::do_something (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Parent doing something\n"))); +} + +size_t Child::instance_count_ = 0; + +Child::Child (ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> parent) + : parent_(parent) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Creating Child object\n"))); + ++Child::instance_count_; +} + +Child::~Child (void) +{ + --Child::instance_count_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Deleting Child object\n"))); +} + +void Child::do_something (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Child doing something\n"))); + + // Using operator-> on a weak pointer will automatically create a strong + // pointer as a temporary. This ensures that the object exists for the + // lifetime of the call (although it does not check for null). + parent_->do_something (); + + // In cases where we may need to call operations on the weak pointer + // many times, we can reduce the overhead by explicitly converting to a + // strong pointer first. + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> strong_parent (parent_); + + // You can check for null to see if the parent object still exists (in this + // case it is not strictly necessary since the child will only exist if the + // parent still exists). + if (strong_parent == 0) + return; + + for (int i = 0; i < 5; ++i) + strong_parent->do_something (); +} + +size_t Printer::instance_count_ = 0; + +Printer::Printer (const char *message) + : message_ (message) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Creating Printer object\n"))); + ++Printer::instance_count_; +} + +Printer::~Printer (void) +{ + --Printer::instance_count_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Deleting Printer object\n"))); +} + +void +Printer::print (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %s\n"), + ACE_TEXT_CHAR_TO_TCHAR(this->message_))); +} + +#if defined (ACE_HAS_THREADS) + +/** + * @class Method_Request_print + * + * @brief Reification of the <print> method. + */ +class Method_Request_print : public ACE_Method_Request +{ +public: + Method_Request_print (Scheduler *, + Printer_var &printer); + virtual ~Method_Request_print (void); + + /// This is the entry point into the Active Object method. + virtual int call (void); + +private: + Scheduler *scheduler_; + Printer_var printer_; +}; + +Method_Request_print::Method_Request_print (Scheduler *new_scheduler, + Printer_var &printer) + : scheduler_ (new_scheduler), + printer_ (printer) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_print created\n"))); +} + +Method_Request_print::~Method_Request_print (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_print will be deleted.\n"))); +} + +int +Method_Request_print::call (void) +{ + // Dispatch the Servant's operation and store the result into the + // Future. + Printer_var temp = printer_; + + temp->print (); + + return 0; +} + +/** + * @class Method_Request_end + * + * @brief Reification of the <end> method. + */ +class Method_Request_end : public ACE_Method_Request +{ +public: + Method_Request_end (Scheduler *new_Prime_Scheduler); + virtual ~Method_Request_end (void); + virtual int call (void); + +private: + Scheduler *scheduler_; +}; + +Method_Request_end::Method_Request_end (Scheduler *scheduler) + : scheduler_ (scheduler) +{ +} + +Method_Request_end::~Method_Request_end (void) +{ +} + +int +Method_Request_end::call (void) +{ + // Shut down the scheduler by deactivating the activation queue's + // underlying message queue - should pop all worker threads off their + // wait and they'll exit. + this->scheduler_->msg_queue ()->deactivate (); + return -1; +} + +// Constructor +// Associates the activation queue with this task's message queue, +// allowing easy access to the message queue for shutting it down +// when it's time to stop this object's service threads. +Scheduler::Scheduler (Scheduler *new_scheduler) + : activation_queue_ (msg_queue ()), scheduler_ (new_scheduler) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Scheduler created\n"))); +} + +// Destructor + +Scheduler::~Scheduler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Scheduler will be destroyed\n"))); +} + +// open + +int +Scheduler::open (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Scheduler open\n"))); + // Become an Active Object. + int num_threads = 3; + return this->activate (THR_BOUND | THR_JOINABLE, num_threads); +} + +// close + +int +Scheduler::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) rundown\n"))); + return 0; +} + +// Service.. + +int +Scheduler::svc (void) +{ + for (;;) + { + // Dequeue the next method request (we use an strong pointer in + // case an exception is thrown in the <call>). + ACE_Strong_Bound_Ptr<ACE_Method_Request, ACE_Null_Mutex> mo (this->activation_queue_.dequeue ()); + if (mo == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) activation queue shut down\n"))); + break; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) calling method request\n"))); + // Call it. + if (mo->call () == -1) + break; + + // Destructor automatically deletes it. + } + + return 0; +} + +void +Scheduler::end (void) +{ + this->activation_queue_.enqueue (new Method_Request_end (this)); +} + +// Here's where the work takes place. + +void +Scheduler::print (Printer_var &printer) +{ + this->activation_queue_.enqueue + (new Method_Request_print (this, + printer)); +} + +// Total number of loops. +static int n_loops = 10; + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bound_Ptr_Test")); + + + // ========================================================================= + // The following test uses the ACE_Strong_Bound_Ptr in a single + // thread of control, hence we use the ACE_Null_Mutex + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) performing synchronous test...\n"))); + + Parent *parent1 = 0; + ACE_NEW_RETURN (parent1, + Parent, + -1); + ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> p8; + { + // Must get the pointer from the parent object's weak_self_ member. + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> p(parent1->weak_self_); + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> p1(p); + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> p2(p); + ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> p3(p); + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> p4(p); + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> p5 = p2; + ACE_Strong_Bound_Ptr<Parent, ACE_Null_Mutex> p6 = p3; + ACE_Weak_Bound_Ptr<Parent, ACE_Null_Mutex> p7(p1); + p8 = p2; + p->child_->do_something (); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Parent instance count is %d, expecting 0\n"), + Parent::instance_count_)); + if (Parent::instance_count_ != 0) + return -1; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Child instance count is %d, expecting 0\n"), + Child::instance_count_)); + if (Child::instance_count_ != 0) + return -1; + // Weak pointer should now be set to null. + ACE_ASSERT (p8.null ()); + + Printer *printer1 = 0; + ACE_NEW_RETURN (printer1, + Printer ("I am printer 1"), + -1); + ACE_Weak_Bound_Ptr<Printer, ACE_Null_Mutex> r9; + { + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r(printer1); + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r1(r); + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r2(r); + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r3(r); + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r4(r); + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r5 = r2; + ACE_Strong_Bound_Ptr<Printer, ACE_Null_Mutex> r6 = r1; + ACE_Weak_Bound_Ptr<Printer, ACE_Null_Mutex> r7(r1); + ACE_Weak_Bound_Ptr<Printer, ACE_Null_Mutex> r8 = r2; + r9 = r3; + r9->print (); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Printer instance count is %d, expecting 0\n"), + Printer::instance_count_)); + ACE_ASSERT (Printer::instance_count_ == 0); + // Weak pointer should now be set to null. + ACE_ASSERT (r9.null ()); + +#if defined (ACE_HAS_THREADS) + + // ========================================================================= + // The following test uses the ACE_Strong_Bound_Ptr in multiple + // threads of control. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) performing asynchronous test...\n"))); + + Scheduler *scheduler_ptr = 0; + + // Create active objects.. + ACE_NEW_RETURN (scheduler_ptr, + Scheduler (), + -1); + + ACE_Strong_Bound_Ptr<Scheduler, ACE_Null_Mutex> scheduler(scheduler_ptr); + + ACE_ASSERT (scheduler->open () != -1); + + { + Printer *printer2 = 0; + ACE_NEW_RETURN (printer2, + Printer ("I am printer 2"), + -1); + + // Ownership is transferred from the auto_ptr to the strong pointer. + auto_ptr<Printer> a (printer2); + Printer_var r (a); + + for (int i = 0; i < n_loops; i++) + // Spawn off the methods, which run in a separate thread as + // active object invocations. + scheduler->print (r); + } + + // Close things down. + scheduler->end (); + + scheduler->wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Printer instance count is %d, expecting 0\n"), + Printer::instance_count_)); + if (Printer::instance_count_ != 0) + return -1; + +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bound_Ptr_Test.h b/ACE/tests/Bound_Ptr_Test.h new file mode 100644 index 00000000000..9b72c7863d1 --- /dev/null +++ b/ACE/tests/Bound_Ptr_Test.h @@ -0,0 +1,87 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Bound_Ptr_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// @author Christopher Kohlhoff <chris@kohlhoff.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_BOUND_PTR_TEST_H +#define ACE_TESTS_BOUND_PTR_TEST_H + +#include "ace/Activation_Queue.h" +#include "ace/Bound_Ptr.h" +#include "ace/Task.h" +#include "ace/Thread_Mutex.h" + +struct Printer +{ + Printer (const char *message); + ~Printer (void) ; + + void print (void); + + const char *message_; + static size_t instance_count_; +}; + +#if defined (ACE_HAS_THREADS) + +typedef ACE_Strong_Bound_Ptr<Printer, ACE_Thread_Mutex> Printer_var; + +/** + * @class Scheduler + * + * @brief The scheduler for the Active Object. + * + * This class also plays the role of the Proxy and the Servant + * in the Active Object pattern. Naturally, these roles could + * be split apart from the Scheduler. + */ +class Scheduler : public ACE_Task<ACE_SYNCH> +{ + + friend class Method_Request_print; + friend class Method_Request_end; +public: + // = Initialization and termination methods. + /// Constructor. + Scheduler (Scheduler * = 0); + + /// Initializer. + virtual int open (void *args = 0); + + /// Terminator. + virtual int close (u_long flags = 0); + + /// Destructor. + virtual ~Scheduler (void); + + // = These methods are part of the Active Object Proxy interface. + void print (Printer_var &printer); + void end (void); + +protected: + /// Runs the Scheduler's event loop, which dequeues <Method_Requests> + /// and dispatches them. + virtual int svc (void); + +private: + // = These are the <Scheduler> implementation details. + ACE_Activation_Queue activation_queue_; + Scheduler *scheduler_; +}; + +#endif /* ACE_HAS_THREADS */ +#endif /* ACE_TESTS_BOUND_PTR_TEST_H */ diff --git a/ACE/tests/Buffer_Stream_Test.cpp b/ACE/tests/Buffer_Stream_Test.cpp new file mode 100644 index 00000000000..ce4dc033801 --- /dev/null +++ b/ACE/tests/Buffer_Stream_Test.cpp @@ -0,0 +1,237 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Buffer_Stream_Test.cpp +// +// = DESCRIPTION +// This program illustrates an implementation of the classic +// "bounded buffer" program using an ASX STREAM containing two +// Modules. Each ACE_Module contains two Tasks. Each ACE_Task +// contains a ACE_Message_Queue and a pointer to a +// ACE_Thread_Manager. Note how the use of these reusable +// components reduces the reliance on global variables. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Doug Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Stream.h" +#include "ace/Module.h" +#include "ace/Task.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_time.h" + +ACE_RCSID(tests, Buffer_Stream_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +typedef ACE_Stream<ACE_MT_SYNCH> MT_Stream; +typedef ACE_Module<ACE_MT_SYNCH> MT_Module; +typedef ACE_Task<ACE_MT_SYNCH> MT_Task; + +class Common_Task : public MT_Task + // = TITLE + // Methods that are common to the Supplier and consumer. +{ +public: + Common_Task (void) {} + + //FUZZ: disable check_for_lack_ACE_OS + // = ACE_Task hooks. + virtual int open (void * = 0); + virtual int close (u_long = 0); + //FUZZ: enable check_for_lack_ACE_OS +}; + +class Supplier : public Common_Task +// = TITLE +// Define the Supplier interface. +{ +public: + Supplier (void) {} + + virtual int svc (void); + // Read data from stdin and pass to consumer. +}; + +class Consumer : public Common_Task + // = TITLE + // Define the Consumer interface. +{ +public: + Consumer (void) {} + + virtual int put (ACE_Message_Block *mb, ACE_Time_Value *tv = 0); + // Enqueue the message on the ACE_Message_Queue for subsequent + // handling in the svc() method. + + virtual int svc (void); + // Receive message from Supplier and print to stdout. +private: + + ACE_Time_Value timeout_; + // Amount of time to wait for a timeout. +}; + +// Spawn off a new thread. + +int +Common_Task::open (void *) +{ + if (this->activate (THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn")), -1); + return 0; +} + +int +Common_Task::close (u_long exit_status) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) thread is exiting with status %d in module %s\n"), + exit_status, + this->name ())); + + // Can do anything here that is required when a thread exits, e.g., + // storing thread-specific information in some other storage + // location, etc. + return 0; +} + +// The Supplier reads data from the stdin stream, creates a message, +// and then queues the message in the message list, where it is +// removed by the consumer thread. A 0-sized message is enqueued when +// there is no more data to read. The consumer uses this as a flag to +// know when to exit. + +int +Supplier::svc (void) +{ + ACE_Message_Block *mb = 0; + + // Send one message for each letter of the alphabet, then send an empty + // message to mark the end. + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + { + // Allocate a new message. + char d[2]; + d[0] = *c; + d[1] = '\0'; + + ACE_NEW_RETURN (mb, + ACE_Message_Block (2), + -1); + ACE_OS::strcpy (mb->wr_ptr (), d); + + mb->wr_ptr (2); + + if (this->put_next (mb) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("put_next"))); + } + + ACE_NEW_RETURN(mb, ACE_Message_Block, -1); + if (this->put_next (mb) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), ACE_TEXT ("put_next"))); + + return 0; +} + +int +Consumer::put (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + // Simply enqueue the Message_Block into the end of the queue. + return this->putq (mb, tv); +} + +// The consumer dequeues a message from the ACE_Message_Queue, writes +// the message to the stderr stream, and deletes the message. The +// Consumer sends a 0-sized message to inform the consumer to stop +// reading and exit. + +int +Consumer::svc (void) +{ + ACE_Message_Block *mb = 0; + int result; + const char *c = ACE_ALPHABET; + char *output = 0; + + // Keep looping, reading a message out of the queue, until we + // timeout or get a message with a length == 0, which signals us to + // quit. + + for (;;) + { + this->timeout_.set (ACE_OS::time (0) + 4, 0); // Wait for upto 4 seconds + + result = this->getq (mb, &this->timeout_); + + if (result == -1) + break; + + size_t const length = mb->length (); + + if (length > 0) + { + output = mb->rd_ptr (); + ACE_ASSERT (*c == output[0]); + c++; + } + mb->release (); + + if (length == 0) + break; + } + +#if !defined (ACE_HAS_WINCE) + ACE_ASSERT (result == 0 || errno == EWOULDBLOCK); +#endif /* ! ACE_HAS_WINCE */ + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Main driver function. + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Buffer_Stream_Test")); + +#if defined (ACE_HAS_THREADS) + // Control hierachically-related active objects. + MT_Stream stream; + MT_Module *cm = 0; + MT_Module *sm = 0; + + // Allocate the Consumer and Supplier modules. + ACE_NEW_RETURN (cm, MT_Module (ACE_TEXT ("Consumer"), new Consumer), -1); + ACE_NEW_RETURN (sm, MT_Module (ACE_TEXT ("Supplier"), new Supplier), -1); + + // Create Supplier and Consumer Modules and push them onto the + // Stream. All processing is performed in the Stream. + + if (stream.push (cm) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("push")), 1); + else if (stream.push (sm) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("push")), 1); + + // Barrier synchronization: wait for the threads to exit, then exit + // ourselves. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Bug_1576_Regression_Test.cpp b/ACE/tests/Bug_1576_Regression_Test.cpp new file mode 100644 index 00000000000..e0986fe9fa9 --- /dev/null +++ b/ACE/tests/Bug_1576_Regression_Test.cpp @@ -0,0 +1,66 @@ +/** + * @file Bug_1576_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 1576: + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=1576 + * + * @author Carlos O'Ryan <coryan@atdesk.com> + */ + +#include "test_config.h" +#include "ace/DLL.h" + +ACE_RCSID (tests, + Bug_1576_Regression_Test, + "$Id$") + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_1576_Regression_Test")); + + ACE_DLL dll; + + const ACE_TCHAR * dll_name = ACE_TEXT ("NOT_A_DLL") ACE_DLL_SUFFIX; + + // Normally applications should check the return value, but if they + // ignore it... + int result = dll.open (dll_name); + + if(result == -1) + { + // Use dll.error() is you want to get the error text, but we don't this in + // this test because else the error is shown on the scoreboard + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Load failed, as expected\n"))); + } + else + { + ACE_ERROR((LM_ERROR, + ACE_TEXT ("Success loading %s ? It should have failed!\n"), + dll_name)); + } + + // ... and then use the DLL library, the program crashes (instead of + // just getting an error ... + void * symbol = dll.symbol (ACE_TEXT ("SHOULD_CRASH")); + + if(symbol == 0) + { + // Use dll.error() is you want to get the error text, but we don't this in + // this test because else the error is shown on the scoreboard + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("Symbol lookup failed, as expected\n"))); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Found symbol ? It should have failed!\n"))); + } + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bug_1890_Regression_Test.cpp b/ACE/tests/Bug_1890_Regression_Test.cpp new file mode 100644 index 00000000000..3f489c5bad2 --- /dev/null +++ b/ACE/tests/Bug_1890_Regression_Test.cpp @@ -0,0 +1,329 @@ +/** + * @file Bug_1890_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 1890 + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=1890 + * + * @author Carlos O'Ryan <coryan@atdesk.com> + * Based on a test provided by "Vadim" (no further details available) + */ + +#include "test_config.h" + +#include "ace/Pipe.h" +#include "ace/Event_Handler.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" + +ACE_RCSID (tests, + Bug_1890_Regression_Test, + "$Id$") + +int const nhandlers = 3; + +/** + * This class is used to create real I/O in the test. To keep the I/O under + * control and keep the test to a single process we use ACE_Pipe. This class + * is known to work with the Reactor, in fact, that is its main function. + * + * Handler counts how many calls to handle_input() has the reactor performed. + * When bug 1890 is triggered the Reactor continues to call the timers, but it + * stops calling select() and the handle_input() functions. + */ +class Handler : public ACE_Event_Handler +{ +public: + Handler(); + + //FUZZ: disable check_for_lack_ACE_OS + /// Initialize the pipe and register with the reactor + int open(ACE_Reactor * reactor); + //FUZZ: enable check_for_lack_ACE_OS + + /// Return the current count + size_t handle_input_count() const; + + /// Write some data + void send_dummy_data(); + + /// Reactor callback + virtual ACE_HANDLE get_handle() const; + virtual int handle_input(ACE_HANDLE); + +private: + size_t handle_input_count_; + + ACE_Pipe the_pipe_; + + ACE_HANDLE handles_[2]; +}; + +/** + * This is the main driver for the test. This timer is called by the reactor + * in a repeating interval. On the first @c initial_iterations the Timer + * writes data through all of its handlers. On iteration @c initial_iteration + * it triggers bug 1890 by removing all the handlers from the reactor, and + * then re-adding one handler. + * + */ +class Timer : public ACE_Event_Handler +{ +public: + Timer(); + + //FUZZ: disable check_for_lack_ACE_OS + int open(ACE_Reactor * reactor); + void close(); + //FUZZ: enable check_for_lack_ACE_OS + + bool check_expected_results() const; + + virtual int handle_timeout(ACE_Time_Value const &, void const*); + +private: + void send_data_through_handlers(); + void remove_some_handlers(); + + Handler & special_handler(); + Handler const & special_handler() const; + +private: + Handler handler_[nhandlers]; + int iteration_; + + size_t recorded_count_; +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_1890_Regression_Test")); + + bool success = true; + + // Bug 1890 is all about ACE_Select_Reactor, so run it on that reactor + // regardless of platform. + ACE_Select_Reactor select_reactor; + ACE_Reactor reactor (&select_reactor); + + // Create the timer, this is the main driver for the test + Timer * timer = new Timer; + + // Initialize the timer and register with the reactor + if (-1 == timer->open (&reactor)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Cannot initialize timer")), + -1); + } + + reactor.run_reactor_event_loop (); + + // Verify that the results are what we expect + if (!(success = timer->check_expected_results ())) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Test failed\n"))); + + // Cleanup + timer->close (); + delete timer; + + ACE_END_TEST; + + return success ? 0 : -1; +} + +Handler::Handler() + : handle_input_count_(0) + , the_pipe_() +{ +} + +int +Handler::open (ACE_Reactor * r) +{ + if (-1 == the_pipe_.open (handles_)) + { + return -1; + } + if (-1 == r->register_handler (this, ACE_Event_Handler::READ_MASK)) + { + return -1; + } + return 0; +} + +size_t +Handler::handle_input_count() const +{ + return handle_input_count_; +} + +void +Handler::send_dummy_data() +{ + char buf[] = "dummy"; + (void) the_pipe_.send (buf, sizeof (buf)); +} + +ACE_HANDLE +Handler::get_handle() const +{ + return the_pipe_.read_handle (); +} + +int +Handler::handle_input(ACE_HANDLE) +{ + ++handle_input_count_; + // ACE_DEBUG((LM_DEBUG, "Handler::handle_input called for %d\n", h)); + return 0; +} + +int const initial_iterations = 5; +int const total_iterations = 10; + +int const special_handler_index = nhandlers - 1; + +Timer::Timer() + : iteration_(0) + , recorded_count_(0) +{ +} + +int +Timer::open (ACE_Reactor * r) +{ + this->reactor (r); + + // Initialize both handles and register them with the reactor for reading. + for (int i = 0; i != nhandlers; ++i) + { + if (-1 == handler_[i].open (r)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Could not open dummy handler %d %p\n"), + i, + ACE_TEXT ("")), + -1); + } + } + + ACE_Time_Value const interval(0, ACE_ONE_SECOND_IN_USECS / 10); + ACE_Time_Value const startup (0, ACE_ONE_SECOND_IN_USECS / 20); + + if (-1 == r->schedule_timer (this, 0, startup, interval)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Could not schedule timer")), + -1); + } + + return 0; +} + +void +Timer::close() +{ + for (int i = 0; i != nhandlers; ++i) + { + this->reactor ()->remove_handler (&handler_[i], + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); + } + this->reactor ()->cancel_timer (this); +} + +bool +Timer::check_expected_results() const +{ + if (this->recorded_count_ < this->special_handler ().handle_input_count ()) + { + return true; + } + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("recorded_count %B, special_handler count %B\n"), + this->recorded_count_, + this->special_handler ().handle_input_count ())); + return false; +} + +int +Timer::handle_timeout(ACE_Time_Value const &, void const *) +{ + if (iteration_ == 0) + { + send_data_through_handlers(); + } + + ++iteration_; + if (iteration_ < initial_iterations) + { + return 0; + } + + if (iteration_ == initial_iterations) + { + remove_some_handlers(); + recorded_count_ = special_handler().handle_input_count(); + return 0; + } + + if (iteration_ < total_iterations) + { + return 0; + } + + reactor()->end_reactor_event_loop(); + + return 0; +} + +void +Timer::send_data_through_handlers() +{ + for (int i = 0; i != nhandlers; ++i) + { + handler_[i].send_dummy_data(); + } +} + +void +Timer::remove_some_handlers() +{ + // The reactor may not get around to callbacks on deletion until the test + // is over. + ACE_Reactor_Mask mask = + ACE_Event_Handler::ALL_EVENTS_MASK | ACE_Event_Handler::DONT_CALL; + for (int i = 0; i != nhandlers; ++i) + { + if (-1 == reactor()->remove_handler(&handler_[i], mask)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Cannot remove handler %d in %p\n"), + i, + ACE_TEXT ("timeout"))); + } + } + + if (-1 == reactor()->register_handler(&special_handler(), + ACE_Event_Handler::ALL_EVENTS_MASK)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Cannot add back special handler in %p\n"), + ACE_TEXT ("timeout"))); + } +} + +Handler & Timer::special_handler() +{ + return handler_[special_handler_index]; +} + +Handler const & Timer::special_handler() const +{ + return handler_[special_handler_index]; +} diff --git a/ACE/tests/Bug_2368_Regression_Test.cpp b/ACE/tests/Bug_2368_Regression_Test.cpp new file mode 100644 index 00000000000..94e11107e7d --- /dev/null +++ b/ACE/tests/Bug_2368_Regression_Test.cpp @@ -0,0 +1,125 @@ +/** + * @file Bug_2368_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2368: + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2368 + * + * @author Johnny Willemsen <jwillemsen@remedy.nl> + */ + +#include "test_config.h" +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Log_Msg.h" +#include "ace/Signal.h" + +ACE_RCSID (tests, + Bug_2368_Regression_Test, + "$Id$") + +static bool handleA_close_called = false; +static bool handleB_close_called = false; + +class My_HandlerA : public ACE_Event_Handler +{ +public: + virtual int handle_close (ACE_HANDLE, + ACE_Reactor_Mask) + { + ACE_DEBUG ((LM_DEBUG, + "Handle close called\n")); + handleA_close_called = true; + + return 0; + } + + virtual int handle_signal (int, + siginfo_t *, + ucontext_t *) + { + ACE_DEBUG ((LM_DEBUG, + "Handle signal called\n")); + + return 0; + } +}; + +class My_HandlerB : public ACE_Event_Handler +{ +public: + virtual int handle_close (ACE_HANDLE, + ACE_Reactor_Mask) + { + ACE_DEBUG ((LM_DEBUG, + "Handle close called\n")); + handleB_close_called = true; + + return 0; + } + + virtual int handle_signal (int, + siginfo_t *, + ucontext_t *) + { + ACE_DEBUG ((LM_DEBUG, + "Handle signal called\n")); + + return 0; + } +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2368_Regression_Test")); + + My_HandlerA my_handlerA; + My_HandlerB my_handlerB; + + // Set up an ACE signal handler. + if (ACE_Reactor::instance ()->register_handler + (SIGINT, + &my_handlerA) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "register_handlerA"), + -1); + + if (ACE_Reactor::instance ()->register_handler + (SIGINT, + &my_handlerB) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "register_handlerB"), + -1); + + ACE_Sig_Action *new_disp = 0; + if (ACE_Reactor::instance ()->remove_handler + (SIGINT, + new_disp) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "remove_handlerB"), + -1); + + if (ACE_Reactor::instance ()->close () == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "close")); + + if (!handleA_close_called) + ACE_ERROR ((LM_ERROR, + "Handle close hasn't been called for A. " + "This test failure caused by the unresolved bug is EXPECTED!\n")); + + if (!handleB_close_called) + ACE_ERROR ((LM_ERROR, + "Handle close hasn't been called for B. " + "This test failure caused by the unresolved bug is EXPECTED!\n")); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bug_2497_Regression_Test.cpp b/ACE/tests/Bug_2497_Regression_Test.cpp new file mode 100644 index 00000000000..d32c96d1660 --- /dev/null +++ b/ACE/tests/Bug_2497_Regression_Test.cpp @@ -0,0 +1,75 @@ +/** + * @file Bug_2497_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2497 + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2497 + * + * @author sergant128@mail.ru + */ + +#include "test_config.h" +#include "ace/Module.h" +#include "ace/Task.h" +#include "ace/Stream.h" + +ACE_RCSID (tests, + Bug_2497_Regression_Test, + "$Id$") + +class Test_Task : public ACE_Task<ACE_SYNCH> +{ +public: + Test_Task( void ) : + _destructorCalled(0) + { + } + + virtual ~Test_Task( void ) + { + ++_destructorCalled; + if (_destructorCalled > 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test_Task::~Test_Task() should be called once!!!\n"))); + } + +private: + int _destructorCalled; +}; + + +class Test_Module : public ACE_Module<ACE_SYNCH> +{ +public: + Test_Module( void ) + { + this->open( ACE_TEXT("Test module"), + &_writerTask, + &_readerTask, + 0, + M_DELETE_NONE ); + } + +private: + Test_Task _writerTask, _readerTask; +}; + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2497_Regression_Test")); + + ACE_Stream<ACE_SYNCH> stream; + + if (stream.push(new Test_Module()) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: push failed\n"))); + } + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bug_2540_Regression_Test.cpp b/ACE/tests/Bug_2540_Regression_Test.cpp new file mode 100644 index 00000000000..66f1f4f4ddb --- /dev/null +++ b/ACE/tests/Bug_2540_Regression_Test.cpp @@ -0,0 +1,321 @@ +/** + * @file Bug_2540_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2540 + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2540 + * + * @author Carlos O'Ryan <coryan@atdesk.com> + * Based on Bug_1890_Regression_Test + */ + +#include "test_config.h" + +#include "ace/Pipe.h" +#include "ace/Event_Handler.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" + +ACE_RCSID (tests, + Bug_2540_Regression_Test, + "$Id$") + +int const nhandlers = 3; + +/** + * This class is used to create real I/O in the test. To keep the I/O under + * control and keep the test to a single process we use ACE_Pipe. This class + * is known to work with the Reactor, in fact, that is its main function. + * + * Handler counts how many calls to handle_input() has the reactor performed. + * When bug 2540 is triggered the Reactor continues to call the timers, but it + * stops calling select() and the handle_input() functions. + */ +class Handler : public ACE_Event_Handler +{ +public: + Handler(); + + //FUZZ: disable check_for_lack_ACE_OS + /// Initialize the pipe and register with the reactor + int open(ACE_Reactor * reactor); + //FUZZ: enable check_for_lack_ACE_OS + + /// Return the current count + size_t handle_input_count() const; + + /// Write some data + void send_dummy_data(); + + /// Removes itself from the reactor on the next call to handle_input() + void simulate_socket_closure(); + + /// Reactor callback + virtual ACE_HANDLE get_handle() const; + virtual int handle_input(ACE_HANDLE); + +private: + bool auto_remove_flag_; + + size_t handle_input_count_; + + ACE_Pipe the_pipe_; + + ACE_HANDLE handles_[2]; +}; + +/** + * This is the main driver for the test. This timer is called by the reactor + * in a repeating interval. On the first @c initial_iterations the Timer + * writes data through all of its handlers. On iteration @c initial_iteration + * it triggers bug 2540 by removing two handlers from the reactor. + * + */ +class Timer : public ACE_Event_Handler +{ +public: + Timer(); + + //FUZZ: disable check_for_lack_ACE_OS + int open(ACE_Reactor * reactor); + void close(); + //FUZZ: enable check_for_lack_ACE_OS + + bool check_expected_results() const; + + virtual int handle_timeout(ACE_Time_Value const &, void const*); + +private: + void send_data_through_handlers(); + void remove_some_handlers(); + + Handler & special_handler(); + Handler const & special_handler() const; + +private: + Handler handler_[nhandlers]; + int iteration_; + + size_t recorded_count_; +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2540_Regression_Test")); + + // Bug 2540 is all about ACE_Select_Reactor, so run it on that reactor + // regardless of platform. In particular, this test relies on a handler + // that doesn't consume ready-to-read data being called back - this won't + // happen with ACE_WFMO_Reactor. + ACE_Select_Reactor select_reactor; + ACE_Reactor reactor (&select_reactor); + + // Create the timer, this is the main driver for the test + Timer * timer = new Timer; + + // Initialize the timer and register with the reactor + if (-1 == timer->open (&reactor)) + { + ACE_ERROR_RETURN ((LM_ERROR, "Cannot initialize timer\n"), -1); + } + + reactor.run_reactor_event_loop (); + + // Verify that the results are what we expect + if (!timer->check_expected_results ()) + { + ACE_ERROR_RETURN ((LM_ERROR, "Test failed\n"), -1); + } + + // Cleanup + timer->close (); + delete timer; + + ACE_END_TEST; + + return 0; +} + +Handler::Handler() + : auto_remove_flag_(false) + , handle_input_count_(0) + , the_pipe_() +{ +} + +int Handler::open(ACE_Reactor * r) +{ + if(-1 == the_pipe_.open(handles_)) + { + return -1; + } + if(-1 == r->register_handler(this, ACE_Event_Handler::READ_MASK)) + { + return -1; + } + return 0; +} + +size_t Handler::handle_input_count() const +{ + return handle_input_count_; +} + +void Handler::send_dummy_data() +{ + char buf[] = "dummy"; + (void) the_pipe_.send(buf, sizeof(buf)); +} + +void Handler::simulate_socket_closure() +{ + auto_remove_flag_ = true; +} + +ACE_HANDLE Handler::get_handle() const +{ + return the_pipe_.read_handle(); +} + +int Handler::handle_input(ACE_HANDLE /* h */) +{ + + ++handle_input_count_; + // ACE_DEBUG((LM_DEBUG, "Handler::handle_input called for %d\n", h)); + + if(auto_remove_flag_) + { + auto_remove_flag_ = false; + return -1; + } + + return 0; +} + +int const initial_iterations = 5; +int const total_iterations = 10; + +int const special_handler_index = nhandlers - 1; + +Timer::Timer() + : iteration_(0) + , recorded_count_(0) +{ +} + +int Timer::open(ACE_Reactor * r) +{ + this->reactor(r); + + // Initialize both handles and register them with the reactor for reading. + for(int i = 0; i != nhandlers; ++i) + { + if (-1 == handler_[i].open(r)) + { + ACE_ERROR_RETURN ((LM_ERROR, "Could not open dummy handler %d\n", i), -1); + } + } + + ACE_Time_Value const interval(0, ACE_ONE_SECOND_IN_USECS / 10); + ACE_Time_Value const startup (0, ACE_ONE_SECOND_IN_USECS / 20); + + if ( -1 == r->schedule_timer(this, 0, startup, interval)) + { + ACE_ERROR_RETURN((LM_ERROR, "Could not schedule timer\n"), -1); + } + + return 0; +} + +void Timer::close() +{ + for(int i = 0; i != nhandlers; ++i) + { + reactor()->remove_handler(&handler_[i], ACE_Event_Handler::ALL_EVENTS_MASK); + } + reactor()->cancel_timer(this); +} + +bool Timer::check_expected_results() const +{ + // We expect at least one more call after the other handlers are removed. + if(recorded_count_ + 1 < special_handler().handle_input_count() ) + { + return true; + } + return false; +} + +int Timer::handle_timeout(ACE_Time_Value const &, void const *) +{ + if (iteration_ == 0) + { + // Sending data on the first iteration makes the handles always + // "ready" for reading because the Handler::handle_input() function + // never consumes the data. + send_data_through_handlers(); + } + + ++iteration_; + if (iteration_ < initial_iterations) + { + // The first iterations are there just to prime things. + return 0; + } + + if (iteration_ == initial_iterations) + { + // We expect the special_handler() to work normally after this + // iteration, i.e., more calls to handle_input() should be delivered + // to it. + recorded_count_ = special_handler().handle_input_count(); + + // Remove the handlers the next time the loop runs + remove_some_handlers(); + + // Run the event loop, this causes the handlers to be removed from the + // reactor, except for special_handler() + ACE_Time_Value interval(0, ACE_ONE_SECOND_IN_USECS / 50); + reactor()->handle_events(&interval); + + return 0; + } + + if (iteration_ < total_iterations) + { + // Run a while more to make sure the special_handler() is used. + return 0; + } + + reactor()->end_reactor_event_loop(); + + return 0; +} + +void Timer::send_data_through_handlers() +{ + for(int i = 0; i != nhandlers; ++i) + { + handler_[i].send_dummy_data(); + } +} + +void Timer::remove_some_handlers() +{ + for(int i = 0; i != nhandlers - 1; ++i) + { + handler_[i].simulate_socket_closure(); + } +} + +Handler & Timer::special_handler() +{ + return handler_[special_handler_index]; +} + +Handler const & Timer::special_handler() const +{ + return handler_[special_handler_index]; +} diff --git a/ACE/tests/Bug_2609_Regression_Test.cpp b/ACE/tests/Bug_2609_Regression_Test.cpp new file mode 100644 index 00000000000..734ba205acd --- /dev/null +++ b/ACE/tests/Bug_2609_Regression_Test.cpp @@ -0,0 +1,148 @@ +/** + * @file Bug_2609_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2609: + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2609 + * + * @author Milan Cvetkovic <milan.cvetkovic@mpathix.com> + */ + +#include "test_config.h" + +ACE_RCSID (tests, + Bug_2609_Regression_Test, + "$Id$") + +#include "ace/Svc_Handler.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/Trace.h" +#include "ace/SOCK_Connector.h" +#include "ace/Thread_Semaphore.h" + +#if defined (ACE_HAS_THREADS) + +# define TEST_TRACE(X) ACE_Trace ____ (ACE_TEXT (X), __LINE__, ACE_TEXT (__FILE__)) + +bool g_handler_deleted = false; +// use semaphore for synchronization +// +ACE_Thread_Semaphore g_semaphore (0); + +class My_Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH> +{ +public: + typedef ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH> super; + My_Svc_Handler() + { + TEST_TRACE ("My_Svc_Handler:My_Svc_Handler"); + g_handler_deleted = false; + reference_counting_policy().value( + Reference_Counting_Policy::ENABLED); + } + + ~My_Svc_Handler() + { + TEST_TRACE ("My_Svc_Handler::~My_Svc_Handler"); + g_handler_deleted = true; + } + + //FUZZ: disable check_for_lack_ACE_OS + int open (void* pv) + { + TEST_TRACE ("open"); + g_semaphore.release(); // signal open completed + return super::open (pv); + } + //FUZZ: enable check_for_lack_ACE_OS + + int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask mask) + { + TEST_TRACE ("handle_close"); + super::handle_close (fd, mask); + if (g_handler_deleted) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Handler deleted in base class' handle_close()\n"))); + } + // signal handle_close() completed + g_semaphore.release(); + return 0; + } +}; + +struct My_Task : public ACE_Task_Base +{ + int svc() + { + TEST_TRACE ("My_Task::svc"); + ACE_Reactor::instance()->owner(ACE_OS::thr_self()); + int rv = ACE_Reactor::instance()->run_reactor_event_loop(); + if (rv < 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT("Cannot run reactor event loop\n"))); + } + return 0; + } +}; + +// event handler used to signal when the reactor started +struct Timer_Handler : public ACE_Event_Handler +{ + int handle_timeout (const ACE_Time_Value&, const void*) + { + g_semaphore.release(); // signal reactor started + return 0; + } +}; + +typedef ACE_Acceptor<My_Svc_Handler, ACE_SOCK_ACCEPTOR> My_Acceptor; + +#endif + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2609_Regression_Test")); + +#if defined (ACE_HAS_THREADS) + + My_Acceptor acceptor (ACE_INET_Addr(9876)); + Timer_Handler timer_handler; + ACE_Reactor::instance()->schedule_timer( + &timer_handler, 0, ACE_Time_Value(0)); + + My_Task task; + int activated = task.activate(); + if (activated < 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Could not activate task\n"))); + } + + g_semaphore.acquire();// wait for reactor to start + { + ACE_SOCK_Connector c1; + ACE_SOCK_Stream s1; + ACE_INET_Addr a1(9876, "localhost"); + if (-1 == c1.connect (s1, a1)) + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT("Could not connect\n")), -1); + } + g_semaphore.acquire(); // wait for open to complete + s1.close(); + g_semaphore.acquire(); // wait for handle_close to complete + } + ACE_Reactor::end_event_loop(); + task.wait(); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bug_2610_Regression_Test.cpp b/ACE/tests/Bug_2610_Regression_Test.cpp new file mode 100644 index 00000000000..648e90bf2d2 --- /dev/null +++ b/ACE/tests/Bug_2610_Regression_Test.cpp @@ -0,0 +1,199 @@ +/** + * @file Bug_2610_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2610: + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2610 + * + * @author Milan Cvetkovic <milan.cvetkovic@mpathix.com> + */ + +#include "test_config.h" + +ACE_RCSID (tests, + Bug_2610_Regression_Test, + "$Id$") + +#include "ace/INET_Addr.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Svc_Handler.h" +#include "ace/Acceptor.h" +#include "ace/Connector.h" +#include "ace/Trace.h" +#include "ace/Thread_Semaphore.h" + +#if defined (ACE_HAS_THREADS) + +# define TEST_TRACE(X) ACE_Trace ____ (ACE_TEXT (X), __LINE__, ACE_TEXT (__FILE__)) + +int g_svc_handlers_leaked = 0; +// use semaphore for synchronization +// +ACE_Thread_Semaphore g_semaphore (0); + +class My_Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH> +{ +public: + typedef ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH> super; + My_Svc_Handler() + { + TEST_TRACE ("My_Svc_Handler:My_Svc_Handler"); + ++g_svc_handlers_leaked; + reference_counting_policy().value( + Reference_Counting_Policy::ENABLED); + } + + ~My_Svc_Handler() + { + TEST_TRACE ("My_Svc_Handler::~My_Svc_Handler"); + --g_svc_handlers_leaked; + } + + int handle_close (ACE_HANDLE /*fd*/, ACE_Reactor_Mask /*mask*/) + { + TEST_TRACE ("handle_close"); + g_semaphore.release(); + return 0; + } +}; + +bool g_acceptor_accept_fails; + +class My_Acceptor : public ACE_Acceptor<My_Svc_Handler, ACE_SOCK_ACCEPTOR> +{ +public: + typedef ACE_Acceptor<My_Svc_Handler, ACE_SOCK_ACCEPTOR> super; + My_Acceptor () : super (ACE_INET_Addr(9000)) {} + +protected: + int accept_svc_handler (My_Svc_Handler *svc_handler) + { + TEST_TRACE ("accept_svc_handler"); + int rv = super::accept_svc_handler(svc_handler); + if (g_acceptor_accept_fails) + { + g_semaphore.release(); + return -1; + } + return rv; + } + int activate_svc_handler (My_Svc_Handler* /*svc_handler*/) + { + TEST_TRACE ("My_Acceptor::activate_svc_handler"); + g_semaphore.release(); + return -1; + } +}; + +class My_Connector : public ACE_Connector<My_Svc_Handler, ACE_SOCK_CONNECTOR> +{ +public: + typedef ACE_Connector<My_Svc_Handler, ACE_SOCK_CONNECTOR> super; +protected: + int activate_svc_handler (My_Svc_Handler* /*svc_handler*/) + { + TEST_TRACE ("My_Connector::activate_svc_handler"); + g_semaphore.release(); + return -1; + } +}; + +struct My_Task : public ACE_Task_Base +{ + int svc() + { + TEST_TRACE ("My_Task::svc"); + ACE_Reactor::instance()->owner(ACE_OS::thr_self()); + int rv = ACE_Reactor::instance()->run_reactor_event_loop(); + if (rv < 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Cannot run reactor event loop\n"))); + } + return 0; + } +}; + +// event handler used to signal when the reactor started +struct Timer_Handler : public ACE_Event_Handler +{ + int handle_timeout (const ACE_Time_Value&, const void*) + { + g_semaphore.release(); // signal reactor started + return 0; + } +}; + +#endif + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2610_Regression_Test")); + +#if defined (ACE_HAS_THREADS) + + My_Acceptor acceptor; + Timer_Handler timer_handler; + ACE_Reactor::instance()->schedule_timer( + &timer_handler, 0, ACE_Time_Value(0)); + + My_Task task; + int activated = task.activate(); + if (activated < 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Could not activate task\n")), -1); + } + + ACE_INET_Addr a1(9000, "localhost"); + ACE_SOCK_Connector c1; + g_semaphore.acquire();// wait for reactor to start + + { + g_acceptor_accept_fails = true; + ACE_SOCK_Stream s1; + if (-1 == c1.connect (s1, a1)) + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT("Could not connect\n")), -1); + } + g_semaphore.acquire(); // wait for accept_svc_handler() to start + } + { + g_acceptor_accept_fails = false; + ACE_SOCK_Stream s1; + if (-1 == c1.connect (s1, a1)) + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT("Could not connect\n")), -1); + } + g_semaphore.acquire(); // wait for activate_svc_handler to complete + } + + { + My_Svc_Handler* handler=0; + My_Connector connector; + connector.connect (handler, a1); + ACE_Event_Handler_var safe_handler (handler); + g_semaphore.acquire(); // wait for connect to complete + } + ACE_Reactor::end_event_loop(); + task.wait(); + + if (g_svc_handlers_leaked != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("Svc_Handler leakage detected, %d objects\n")), + g_svc_handlers_leaked); + } +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bug_2653_Regression_Test.cpp b/ACE/tests/Bug_2653_Regression_Test.cpp new file mode 100644 index 00000000000..6a86198f6f2 --- /dev/null +++ b/ACE/tests/Bug_2653_Regression_Test.cpp @@ -0,0 +1,236 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Bug_2653_Regression_Test.cpp +// +// = DESCRIPTION +// This bug occurs when schedule_wakeup is called for a handle that does +// not already have an event handler registered. This can happen quite +// legitimately in multithreaded applications where one thread schedules +// the wakeup while another thread is handling the closure of the +// connection and unregistering. +// +// = AUTHOR +// Phil Mesnier <mesnier_p@ociweb.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/Pipe.h" +#include "ace/ACE.h" +#include "ace/Task.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Bug_2653_Regression_Test, "$Id$") + +static const char *message = +"Hello there! Hope you get this message"; + +class Watchdog : public ACE_Task_Base +{ +public: + int svc (void); + int my_grp_; +}; + +int +Watchdog::svc (void) +{ + ACE_OS::sleep (5); + // If we make it through the sleep and haven't been canceled, that + // means the process is hung. + if (!this->thr_mgr ()->testcancel (ACE_Thread::self ())) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Watchdog slept without cancel - we're hung\n"))); + return 0; +} + +class Handler : public ACE_Event_Handler +{ +public: + Handler (ACE_Reactor &reactor, bool close_other); + + ~Handler(); + + int handle_input (ACE_HANDLE fd); + + int handle_output (ACE_HANDLE fd); + + ACE_Pipe pipe_; + ACE_Pipe other_pipe_; +}; + +Handler::Handler (ACE_Reactor &reactor, bool close_other) + : ACE_Event_Handler (&reactor) +{ + // Create the pipe. + bool ok = true; + if (0 != this->other_pipe_.open () || 0 != this->pipe_.open()) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("pipe"))); + ok = false; + } + else + { + // Register for all events. + if (0 != this->reactor ()->register_handler + (this->pipe_.read_handle (), + this, + ACE_Event_Handler::READ_MASK)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("register"))); + ok = false; + } + + if (-1 == this->reactor ()->schedule_wakeup + (this->other_pipe_.write_handle(), + ACE_Event_Handler::WRITE_MASK)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("Schedule"))); + ok = false; + } + + // closing the other pipe sets up the spinner error. + // leaving it open sets up the segv. + if (close_other) + this->other_pipe_.close(); + + } +} + +Handler::~Handler (void) +{ + this->other_pipe_.close(); + this->pipe_.close (); +} + +int +Handler::handle_output (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_output\n"))); + +#if defined (__OpenBSD__) || defined (ACE_VXWORKS) || defined (__Lynx__) + // All that we need written has been written, so don't + // call handle_output again. + this->reactor ()->mask_ops (this->pipe_.read_handle (), + ACE_Event_Handler::WRITE_MASK, + ACE_Reactor::CLR_MASK); +#endif /* __OpenBSD__ || ACE_VXWORKS || __Lynx__ */ + + return 0; +} + +int +Handler::handle_input (ACE_HANDLE fd) +{ + char buffer[BUFSIZ]; + ssize_t result = ACE::recv (fd, buffer, sizeof buffer); + + if (result != ssize_t (ACE_OS::strlen (message))) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler recv'd %b bytes; expected %B\n"), + result, ACE_OS::strlen (message))); + buffer[result] = '\0'; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_input: %C\n"), buffer)); + + if (ACE_OS::strcmp (buffer, message) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Handler text mismatch; received \"%C\"; ") + ACE_TEXT ("expected \"%C\"\n"), + buffer, message)); + + this->reactor ()->end_reactor_event_loop (); + return 0; +} + +static void +test_for_crash (ACE_Reactor &reactor) +{ + Handler handler (reactor, false); + + // This should trigger a call to <handle_input>. + ssize_t result = + ACE::send_n (handler.pipe_.write_handle (), + message, + ACE_OS::strlen (message)); + if (result != ssize_t (ACE_OS::strlen (message))) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler sent %b bytes; should be %B\n"), + result, ACE_OS::strlen (message))); + + reactor.run_reactor_event_loop (); + + if (0 != reactor.remove_handler (handler.pipe_.read_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test_for_handler, remove pipe"))); + + if (0 == reactor.remove_handler (handler.other_pipe_.write_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("test_for_crash remove other_pipe succeeded ") + ACE_TEXT ("but shouldn't\n"))); +} + +static void +test_for_spin (ACE_Reactor &reactor) +{ + Handler handler (reactor, true); + + // This should trigger a call to <handle_input>. + ssize_t result = + ACE::send_n (handler.pipe_.write_handle (), + message, + ACE_OS::strlen (message)); + if (result != ssize_t (ACE_OS::strlen (message))) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler sent %b bytes; should be %B\n"), + result, ACE_OS::strlen (message))); + + reactor.run_reactor_event_loop (); + + if (0 != reactor.remove_handler (handler.pipe_.read_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test_for_spin, remove pipe"))); + + if (0 == reactor.remove_handler (handler.other_pipe_.write_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("test_for_spin remove other_pipe succeeded ") + ACE_TEXT ("but shouldn't\n"))); +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2653_Regression_Test")); + + ACE_TP_Reactor tp_reactor_impl; + ACE_Reactor tp_reactor (&tp_reactor_impl); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing for crash\n"))); + test_for_crash (tp_reactor); + // if that passes, start the watchdog. We don't need to wait + Watchdog wd; + wd.activate (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing for spin\n"))); + test_for_spin(tp_reactor); + // If test_for_spin returns, all is well. + wd.thr_mgr ()->cancel_grp (wd.grp_id ()); + wd.wait (); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Bug_2659_Regression_Test.cpp b/ACE/tests/Bug_2659_Regression_Test.cpp new file mode 100644 index 00000000000..10be56ada27 --- /dev/null +++ b/ACE/tests/Bug_2659_Regression_Test.cpp @@ -0,0 +1,190 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Bug_2659_Regression_Test.cpp +// +// = DESCRIPTION +// Reproduces the problems reported in bug 2659: +// http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2659 +// +// = AUTHOR +// Ciju John <johnc at ociweb> +// +// ============================================================================ + +#include "test_config.h" + +#include "ace/Log_Msg.h" +#include "ace/Task.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Thread_Manager.h" +#include "ace/TP_Reactor.h" + +#if !defined (ACE_LACKS_UNIX_SIGNALS) + +bool reactor_task_ready = false; + + +//#define RUN_DEBUG 1 + +#if defined RUN_DEBUG + +#define EE_DEBUG(CNAME,MNAME,LOC) \ +EntryExit ee (CNAME,MNAME,LOC) + +class EntryExit +{ +public: + EntryExit (const ACE_TCHAR* class_name, + const ACE_TCHAR *method_name, + void *location = 0) + { + class_name_ [20] = method_name_[20] = 0; + + ACE_OS::strncpy (class_name_, class_name, 20); + ACE_OS::strncpy (method_name_, method_name, 20); + location_ = location; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Entry %@ %s::%s\n") + , location, class_name, method_name)); + }; + + ~EntryExit () + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Exit %@ %s::%s\n") + , location_, class_name_, method_name_)); + }; + +private: + ACE_TCHAR class_name_[21]; + ACE_TCHAR method_name_[21]; + void *location_; +}; + +#else + +#define EE_DEBUG(CNAME,MNAME,LOC) + +#endif // if defined RUN_DEBUG + + + +static void +handle_signal (int ) +{ + EE_DEBUG ("", "handle_signal", 0); + + // Dummy signal handler +} + +class ReactorTask : public ACE_Task_Base +{ +public: + + virtual ~ReactorTask () + { + EE_DEBUG ("ReactorTask", "~ReactorTask", this); + }; + + virtual int svc (void ) + { + EE_DEBUG ("ReactorTask", "svc", this); + + // Register a valid signal handler + // so process doesn't die upon receiving signal + ACE_Sig_Action sa ((ACE_SignalHandler) &::handle_signal, SIGUSR1); + ACE_UNUSED_ARG (sa); + + if (simulate_perform_work () == -1) { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) ERROR: simulated_perform_work failed.\n")) + , -1); + } + + return 0; + }; + +private: + int simulate_perform_work () + { + EE_DEBUG ("ReactorTask", "simulate_perform_work", this); + + // Create a reactor which doesn't automatically restart + // upon interruption + ACE_TP_Reactor tp_reactor (ACE_TP_Reactor::DEFAULT_SIZE, 0); + + reactor_task_ready = true; + + // This will return upon signal interruption + return tp_reactor.handle_events (); + } +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2659_Regression_Test")); + EE_DEBUG ("", "run_main", 0); + + ReactorTask reactor_task; + + if (reactor_task.activate () == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Task activation failed.\n")) + , -1); + } + + ACE_Thread_Manager *thread_manager = reactor_task.thr_mgr (); + if (thread_manager == 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) No Thread Manager found.\n")) + , -1); + } + + while (!reactor_task_ready) + { + ACE_OS::sleep (1); + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Signalling task.\n"))); + + if (thread_manager->kill_all (SIGUSR1) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Task signalling failed.\n")), + -1); + } + + // Wait 5 seconds for the other thread to exit, if it didn't exit the + // wait return -1 and we return with an error + ACE_Time_Value timeout (5); + if (thread_manager->wait (&timeout, false, false) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Error, task wait failed.\n")), + -1); + } + + ACE_END_TEST; + + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2659_Regression_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("The Unix Signals capability is not supported on this platform\n"))); + ACE_END_TEST; + return 0; +} +#endif /* !defined (ACE_LACKS_UNIX_SIGNALS) */ diff --git a/ACE/tests/Bug_2815_Regression_Test.cpp b/ACE/tests/Bug_2815_Regression_Test.cpp new file mode 100644 index 00000000000..d365b4f3d3a --- /dev/null +++ b/ACE/tests/Bug_2815_Regression_Test.cpp @@ -0,0 +1,498 @@ +/** + * @file Bug_2815_Regression_Test.cpp + * + * $Id$ + * + * Verify that the notification queue can be used with large numbers + * of event handlers. + * + * Normally the ACE_Reactor uses a pipe to implement the notify() + * methods. ACE can be compiled with + * ACE_HAS_REACTOR_NOTIFICATION_QUEUE, with this configuration flag + * the Reactor uses a user-space queue to contain the notifications. + * A single message is sent through the pipe to indicate "pipe not + * empty." + * + * In this configuration, if an event handler is removed + * from the Reactor the user-space queue has to be searched for + * pending notifications and the notifications must be removed. + * + * The original implementation used a naive algorithm to search and + * remove the handlers, which resulted in very high overhead when + * removing handlers while having a very long notification queue. + * + * @author Carlos O'Ryan <coryan@atdesk.com> + * + */ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/Select_Reactor.h" + +ACE_RCSID(tests, + Bug_2815_Regression_Test, "$Id$") + +class One_Shot_Handler; + +/** + * @class Driver + * + * @brief Main driver for the test, generates notification events and + * verifies they are received correctly. + * + */ +class Driver +{ +public: + Driver(ACE_Reactor * reactor, + int max_notifications, + char const *test_name); + + /// Run the test + void run (void); + + /// One of the sub-handlers has received a notification + void notification_received (); + + /// One of the sub-handlers has decided to skip several notifications + void notifications_skipped (int skip_count); + + /** + * @brief Return the reactor configured for this test + */ + ACE_Reactor * reactor (); + +private: + /** + * @brief Implement a single iteration. + * + * Each iteration of the test consists of sending multiple + * notifications simultaneously. + */ + void send_notifications (void); + + /** + * @brief Return true if the test is finished. + */ + bool done (void) const; + + /** + * @brief Return true if there are more iterations to run. + */ + bool more_iterations () const; + + /** + * @brief Return true if the current iteration is completed. + */ + bool current_iteration_done () const; + + /** + * @brief Run one iteration of the test, each iteration doubles + * the number of events. + */ + int run_one_iteration (void); + + /** + * @brief Initialize a bunch of One_Shot_Handlers + */ + void initialize_handlers( + int nhandlers, One_Shot_Handler ** handlers); + + /** + * @brief Dispatch events to the One_Shot_Handlers + */ + void notify_handlers( + int nhandlers, One_Shot_Handler ** handlers); + + /** + * @brief Helpful for debugging + * + * The number of notifications received, skipped and sent are + * subject to simple invariants. During debugging any violation of + * those invariants indicates a problem in the application or the + * Reactor. + */ + void check_notification_invariants(); + + /// A good place to set break points. + void invariant_failed(); + +private: + /** + * @brief The reactor used in this test + */ + ACE_Reactor * reactor_; + + /** + * @brief The maximum number of notifications in any single + * iteration. + */ + int max_notifications_; + + /** + * @brief The name of the test + */ + char const * test_name_; + + /** + * @brief Number of notifications received + */ + int notifications_sent_; + + /** + * @brief Number of notifications sent + */ + int notifications_recv_; + + /** + * @brief Number of notifications skipped because + * the handler was removed + */ + int notifications_skipped_; + + /** + * @brief Number of notifications sent on each iteration + */ + int notifications_curr_; +}; + +/** + * @class One_Shot_Handler + * + * @brief A handler that removes itself from the reactor + * after its first notification. + * + * To demonstrate the problems with the first implementation of + * the notification queue we generate multiple event handlers. + * Then we generate multiple notifications for each, but the handlers + * remove themselves from the reactor when the first notification + * is delivered. This causes a lot of activity in the notification + * queue. + * + */ +class One_Shot_Handler : public ACE_Event_Handler +{ +public: + One_Shot_Handler( + Driver * master_handler, + char const * test_name, + int id); + + /// Increase the number of expected notifications + void notification_queued(); + + /// Receive the notifications, but remove itself from the reactor on + /// on the first one. + virtual int handle_exception(ACE_HANDLE); + +private: + /// The driver for this test, communicate results to it + Driver * master_handler_; + + /// The number of expected notifications + int expected_notifications_; + + /// Identify the test and handler for debugging and better error output + char const * test_name_; + int id_; +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2815_Regression_Test")); + +#if !defined(ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Notification queue disabled. ") + ACE_TEXT ("This test depends on purge_pending_notifications. ") + ACE_TEXT ("Thus, the test is disabled in this case\n"))); + +#else + int max_notifications = 512 * 1024; + + { + ACE_Reactor select_reactor ( + new ACE_Select_Reactor, + 1); + + Driver handler(&select_reactor, + max_notifications, + "Select_Reactor"); + + handler.run (); + } + + { + ACE_Reactor tp_reactor (new ACE_TP_Reactor, + 1); + Driver handler(&tp_reactor, + max_notifications, + "TP_Reactor"); + handler.run(); + } + +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + + return 0; +} + +Driver::Driver ( + ACE_Reactor * reactor, + int max_notifications, + char const * test_name) + : reactor_(reactor) + , max_notifications_(max_notifications) + , test_name_(test_name) + , notifications_sent_(0) + , notifications_recv_(0) + , notifications_skipped_(0) + , notifications_curr_(1) +{ +} + +void +Driver::run (void) +{ + while(more_iterations()) + { + if(run_one_iteration() == -1) + { + return; + } + + notifications_curr_ *= 2; + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Test %C passed sent=%d, recv=%d, skip=%d\n"), + test_name_, + notifications_sent_, + notifications_recv_, + notifications_skipped_)); +} + +void +Driver::notification_received () +{ + ++notifications_recv_; + check_notification_invariants(); +} + +void +Driver::notifications_skipped (int skip_count) +{ + notifications_skipped_ += skip_count; + check_notification_invariants(); +} + +ACE_Reactor * +Driver::reactor() +{ + return reactor_; +} + +void +Driver::send_notifications (void) +{ + int const nhandlers = 16; + One_Shot_Handler * handlers[nhandlers]; + initialize_handlers(nhandlers, handlers); + + for (int i = 0; i != notifications_curr_; ++i) + { + notify_handlers(nhandlers, handlers); + } +} + +bool +Driver::done (void) const +{ + return !more_iterations() && current_iteration_done(); +} + +bool +Driver::more_iterations() const +{ + return notifications_curr_ < max_notifications_; +} + +bool +Driver::current_iteration_done() const +{ + return notifications_sent_ == (notifications_recv_ + notifications_skipped_); +} + +int +Driver::run_one_iteration (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running iteration with %d events for %C test\n"), + notifications_curr_, + test_name_)); + + send_notifications (); + + // Run for 30 seconds or until the test is done. + + ACE_Time_Value const timeout(30,0); + + while (!current_iteration_done()) + { + ACE_Time_Value start = ACE_OS::gettimeofday(); + ACE_Time_Value interval(1,0); + reactor()->run_reactor_event_loop(interval); + ACE_Time_Value end = ACE_OS::gettimeofday(); + + if (end - start >= timeout) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test %C failed due to timeout ") + ACE_TEXT (" sent=%d,recv=%d,skip=%d\n"), + test_name_, + notifications_sent_, + notifications_recv_, + notifications_skipped_)); + return -1; + } + } + + return 0; +} + +void +Driver::initialize_handlers( + int nhandlers, One_Shot_Handler ** handlers) +{ + for (int j = 0; j != nhandlers; ++j) + { + handlers[j] = new One_Shot_Handler(this, test_name_, j); + } +} + +void +Driver::notify_handlers( + int nhandlers, One_Shot_Handler ** handlers) +{ + for(int i = 0; i != nhandlers; ++i) + { + if(reactor()->notify (handlers[i]) == -1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT ("Cannot send notifications in %C test (%d/%d)\n"), + test_name_, i, notifications_curr_)); + return; + } + handlers[i]->notification_queued(); + + ++notifications_sent_; + } +} + +void Driver:: +check_notification_invariants() +{ + if (notifications_sent_ < 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("The number of notifications sent (%d)") + ACE_TEXT(" should be positive\n"), + notifications_sent_)); + invariant_failed(); + } + + if (notifications_recv_ < 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("The number of notifications received (%d)") + ACE_TEXT(" should be positive\n"), + notifications_recv_)); + invariant_failed(); + } + + if (notifications_skipped_ < 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("The number of notifications skipped (%d)") + ACE_TEXT(" should be positive\n"), + notifications_skipped_)); + invariant_failed(); + } + + if (notifications_sent_ < notifications_recv_) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Too many notifications received (%d)") + ACE_TEXT(" vs sent (%d)\n"), + notifications_recv_, notifications_sent_)); + invariant_failed(); + } + + if (notifications_sent_ < notifications_skipped_) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Too many notifications skipped (%d)") + ACE_TEXT(" vs sent (%d)\n"), + notifications_skipped_, notifications_sent_)); + invariant_failed(); + } + + if (notifications_skipped_ + notifications_recv_ > notifications_sent_) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Too many notifications skipped (%d)") + ACE_TEXT(" and received (%d) vs sent (%d)\n"), + notifications_skipped_, notifications_recv_, + notifications_sent_)); + invariant_failed(); + } +} + +void Driver:: +invariant_failed() +{ + // Just a good place to set a breakpoint +} + +// ============================================ + +One_Shot_Handler::One_Shot_Handler( + Driver * master_handler, + char const * test_name, int id) + : ACE_Event_Handler(master_handler->reactor()) + , master_handler_(master_handler) + , expected_notifications_(0) + , test_name_(test_name) + , id_(id) +{ +} + +void One_Shot_Handler:: +notification_queued() +{ + ++expected_notifications_; +} + +int One_Shot_Handler:: +handle_exception(ACE_HANDLE) +{ + --expected_notifications_; + master_handler_->notification_received(); + + int r = reactor()->purge_pending_notifications(this); + if (r >= 0) + { + master_handler_->notifications_skipped(r); + delete this; + return 0; + } + + ACE_ERROR((LM_ERROR, + ACE_TEXT ("Cannot remove handler %d in %C test\n"), + id_, test_name_)); + + delete this; + return 0; +} diff --git a/ACE/tests/Bug_2820_Regression_Test.cpp b/ACE/tests/Bug_2820_Regression_Test.cpp new file mode 100644 index 00000000000..93a9c1bc45c --- /dev/null +++ b/ACE/tests/Bug_2820_Regression_Test.cpp @@ -0,0 +1,141 @@ +/** + * @file Bug_2820_Regression_Test.cpp + * + * $Id$ + * + * Verify that the event handler reference counting works correctly + * when the reactor is destroyed. + * + * Pushing a notification through the reactor increments the reference + * count on the target event handler. Both dispatching and purging + * the notification decrement the reference count. However, + * destroying the reactor used to not decrement the reference count. + * This test reproduces the problem and serves as a regression for it. + * + * @author Carlos O'Ryan <coryan@atdesk.com> + * + */ + +#include "test_config.h" +#include "ace/Auto_Ptr.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" + +ACE_RCSID(tests, + Bug_2820_Regression_Test, "$Id$") + +/** + * @class Simple_Handler + * + * @brief A simple event handler for the test + * + */ +class Simple_Handler : public ACE_Event_Handler +{ +public: + /// Constructor + Simple_Handler(ACE_Reactor * reactor); + + /// Destructor + ~Simple_Handler(); + + /// Receive (and ignore) the notifications + virtual int handle_exception(ACE_HANDLE); +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2820_Regression_Test")); + + int result = 0; + + auto_ptr<ACE_Reactor> reactor( + new ACE_Reactor(new ACE_Select_Reactor, 1)); + + ACE_Event_Handler_var v( + new Simple_Handler(reactor.get())); + + ACE_Event_Handler::Reference_Count pre_notify_count = + v->add_reference(); + + int const notify_count = 4; + for(int i = 0; i != notify_count; ++i) + { + reactor->notify(v.handler()); + } + + ACE_Event_Handler::Reference_Count pos_notify_count = + v->add_reference(); + + if(pos_notify_count != pre_notify_count + notify_count + 1) + { + result = -1; + ACE_ERROR((LM_ERROR, + ACE_TEXT("Reference count should increase by %d.") + ACE_TEXT(" Initial count=%d, final count = %d\n"), + notify_count, pre_notify_count, pos_notify_count)); + } + + ACE_auto_ptr_reset(reactor, (ACE_Reactor*)0); + + // Reset the reactor in the event handler, since it is gone.p + v->reactor(0); + + ACE_Event_Handler::Reference_Count pos_release_count = + v->add_reference(); + + // Only our explicit calls to add_reference() should be reflected in + // the refence_count... + if (pos_release_count != pre_notify_count + 2) + { + result = -1; + ACE_ERROR((LM_ERROR, + ACE_TEXT("Reference count should have increased by 2.") + ACE_TEXT(" Initial count=%d, final count = %d\n"), + pre_notify_count, pos_release_count)); + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT("Ref count results. pre_notify refcount=%d,") + ACE_TEXT(" pos_notify=%d, pos_delete=%d\n"), + pre_notify_count, pos_notify_count, pos_release_count)); + + // Remove a reference for each time we explicitly increased it. + v->remove_reference(); + v->remove_reference(); + ACE_Event_Handler::Reference_Count pos_remove_count = + v->remove_reference(); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT("Ref count results. pre_notify refcount=%d,") + ACE_TEXT(" pos_notify=%d, pos_delete=%d, pos_remove=%d\n"), + pre_notify_count, pos_notify_count, pos_release_count, + pos_remove_count)); + + ACE_END_TEST; + + return result; +} + +// ============================================ + +Simple_Handler:: +Simple_Handler( + ACE_Reactor * r) + : ACE_Event_Handler(r) +{ + reference_counting_policy().value( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); +} + +Simple_Handler:: +~Simple_Handler() +{ +} + +int Simple_Handler:: +handle_exception(ACE_HANDLE) +{ + return 0; +} diff --git a/ACE/tests/Bug_2975_Regression_Test.cpp b/ACE/tests/Bug_2975_Regression_Test.cpp new file mode 100644 index 00000000000..dec427fad9b --- /dev/null +++ b/ACE/tests/Bug_2975_Regression_Test.cpp @@ -0,0 +1,61 @@ +/** + * @file Bug_2975_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2975: + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2975 + * + * @author Pavel Zaichenko <zaichep@mail.ru> + */ + +#include "ace/ACE.h" +#include "tests/test_config.h" +#include "ace/Service_Config.h" +#include "ace/ARGV.h" + +ACE_RCSID (tests, + Bug_2975_Regression_Test, + "$Id$") + +int +run_main (int, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2975_Regression_Test")); + + int ret = 0; + + ACE_ARGV args; + args.add (argv[0], true); + args.add (ACE_TEXT("-S")); +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + args.add ( + ACE_TEXT ("\"dynamic ACE_Logger Service_Object * ") + ACE_TEXT ("ACE:_make_ACE_Logging_Strategy() ") + ACE_TEXT ("'-p ~TRACE|DEBUG|INFO|WARNING|NOTICE|ERROR|CRITICAL|ALERT'") + ACE_TEXT ("\"")); +#else // not (ACE_USES_CLASSIC_SVC_CONF == 1) + args.add ( + ACE_TEXT ("'<ACE_Svc_Conf>") + ACE_TEXT ("<dynamic id=\"ACE_Logger\"") + ACE_TEXT (" type=\"Service_Object\">") + ACE_TEXT ("<initializer path=\"ACE\"") + ACE_TEXT (" init=\"_make_ACE_Logging_Strategy\"") + ACE_TEXT (" params=\"-p ~TRACE|DEBUG|INFO|WARNING|NOTICE|ERROR|CRITICAL|ALERT\"/>") + ACE_TEXT ("</dynamic>") + ACE_TEXT ("</ACE_Svc_Conf>'")); +#endif // not (ACE_USES_CLASSIC_SVC_CONF == 1) + + ret = ACE_Service_Config::open(args.argc(), args.argv(), + ACE_DEFAULT_LOGGER_KEY, + 1, 1); + + if (0 != ret) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("ACE_Service_Config::open() failed\n"))); + } + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Bug_3102_Regression_Test.cpp b/ACE/tests/Bug_3102_Regression_Test.cpp new file mode 100644 index 00000000000..5b081469be2 --- /dev/null +++ b/ACE/tests/Bug_3102_Regression_Test.cpp @@ -0,0 +1,59 @@ +/** + * @file Bug_3102_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 3102 + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=3102 + */ + +#include "test_config.h" +#include "ace/ACE.h" +#include "ace/Trace.h" +#include "ace/Thread_Mutex.h" +#include "ace/Managed_Object.h" +#include "ace/Guard_T.h" +#include "ace/OS_NS_stdio.h" + +ACE_RCSID (tests, + Bug_3102_Regression_Test, + "$Id$") + +#if defined (ACE_HAS_THREADS) + +void mutexTest(void) +{ + ACE_MT(ACE_Thread_Mutex *lock = + ACE_Managed_Object<ACE_Thread_Mutex>::get_preallocated_object + (ACE_Object_Manager::ACE_THREAD_EXIT_LOCK)); + ACE_GUARD (ACE_Thread_Mutex, ace_mon, *lock); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Mutex'd!\n"))); +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_3102_Regression_Test")); + +#if defined (ACE_HAS_THREADS) + ACE_Trace::start_tracing(); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Hello world!\n"))); + + mutexTest(); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Done!\n"))); + + ACE_Trace::stop_tracing(); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/CDR_Array_Test.cpp b/ACE/tests/CDR_Array_Test.cpp new file mode 100644 index 00000000000..f6aa7d02e0e --- /dev/null +++ b/ACE/tests/CDR_Array_Test.cpp @@ -0,0 +1,1010 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// CDR_Array_Test.cpp +// +// = DESCRIPTION +// Checks ACE_OutputCDR::write_XX_array. +// Checks ACE_InputCDR::read_XX_array. +// Checks operator<< and operator>> for CDR Streams in +// each of the basic CDR types. +// Gives a measure of the speed of the ACE CDR streams wrt those +// operations. +// +// = AUTHORS +// Cristian Ferretti <cristian_ferretti@yahoo.com> +// +// ============================================================================ + +// For measuring time, choose your method: +// Define: +// +// * USE_GETRUSAGE for using ACE_OS::getrusage. +// Ticks only when process is running. +// +// * USE_CLOCK for using clock(2). +// You're on your own, no ACE_OS:: support. +// Ticks only when process is running. +// +// * None of the above for using ACE_High_Res_Timer. +// Ticks independent of running state of process. + +// #define USE_CLOCK +// #define USE_GETRUSAGE + +#if defined(USE_CLOCK) +#include <time.h> +#endif + +#include "test_config.h" +#include "ace/OS_Memory.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/Get_Opt.h" +#include "ace/CDR_Stream.h" +#include "ace/High_Res_Timer.h" +#include "ace/ACE.h" + +#if defined(USE_GETRUSAGE) && !defined(ACE_HAS_GETRUSAGE) +#error "Can't define USE_GETRUSAGE on this platform." +#endif + +ACE_RCSID(tests, CDR_Array_Test, "$Id$") + +// Default number of elements for check buffer, for each tested CDR type. +// Be aware that time will be affected by the buffer fitting/not fitting +// in the cache (ie, if default_total*sizeof(T) bytes fit in the cache). +// Also, you want that your time measuring method has a resolution +// compatible with this buffer size, if not you will end up measuring 0. +// You can change this value with -t option. +static const int default_total = 32*1024; + +// Repeat this many times for each tested CDR type. +// We then take the average time that took for each type and report that. +// You can change this value with -n option. +static const int default_niter = 10; + +// +// A simple cronometer in seconds, that encapsulates our time +// measuring method. +// +class Crono { +public: + Crono() {} + ~Crono() {} + void start () + { +#if defined(USE_CLOCK) + start_ = clock (); +#elif defined(USE_GETRUSAGE) + ACE_OS::getrusage (RUSAGE_SELF, &start_); +#else + timer.start (); +#endif + } + void stop () + { +#if defined(USE_CLOCK) + end_ = clock (); +#elif defined(USE_GETRUSAGE) + ACE_OS::getrusage (RUSAGE_SELF, &end_); +#else + timer.stop (); +#endif + } + double read_seconds () + { +#if defined(USE_CLOCK) + return (end_ - start_) / (double) CLOCKS_PER_SEC; +#elif defined(USE_GETRUSAGE) + timeval diff; + diff.tv_sec = end_.ru_utime.tv_sec - start_.ru_utime.tv_sec; + diff.tv_usec = end_.ru_utime.tv_usec - start_.ru_utime.tv_usec; + while (diff.tv_usec < 0) + { + --diff.tv_sec; + diff.tv_usec += ACE_ONE_SECOND_IN_USECS; + } + + return diff.tv_sec + diff.tv_usec / double(ACE_ONE_SECOND_IN_USECS); +#else + ACE_Time_Value tv; + timer.elapsed_time(tv); + return tv.usec () / 1000000.0; +#endif + } +private: +#if defined(USE_CLOCK) + clock_t start_; + clock_t end_; +#elif defined(USE_GETRUSAGE) + ACE_Rusage start_; + ACE_Rusage end_; +#else + ACE_High_Res_Timer timer; +#endif +}; + +// +// Our test, performed in the constructor. +// T is one of the CDR types. +// H is a helper class (described later). +// +// All this stuff is in a class and not in template functions +// to avoid having to deal with potential template function +// instantiations problems. +// +template<class T, class H> class CDR_Test +{ +public: + CDR_Test (int total, int niter, int use_array); + static void do_test (int total, int niter, int use_array, + char* srcbuf, char* dstbuf, + int src_offset = 0, int dst_offset = 0); + ~CDR_Test (); + + static void ttoh (const T& t, char* s); + static T checkval(int i); + +private: + CDR_Test (const CDR_Test<T, H>&); + CDR_Test<T, H>& operator= (const CDR_Test<T, H>&); +}; + +static ACE_UINT32 seal = 0xdeadbeef; + +void +zero (char* p, size_t k) +{ + char* end = p + k; + while (p < end) + { + *p++ = '\0'; + } +} + +inline int +mymax (int a, int b) +{ + return (a >= b) ? a : b; +} + +void +memabort () +{ + ACE_ERROR((LM_ERROR, + ACE_TEXT ("new failed, aborting\n"))); + ACE_OS::exit (1); +} + +template<class T, class H> +CDR_Test<T, H>::CDR_Test (int total, int niter, int use_array) +{ + if (total <= 0) + { + return ; + } + + char* srcbuf; + char* dstbuf; + { + const size_t stotal = + (total + 10) * H::size () + sizeof(ACE_UINT32) + ACE_CDR::MAX_ALIGNMENT; + + ACE_NEW(srcbuf, char[stotal]); + if (srcbuf == 0) + { + memabort (); + } + zero(srcbuf, stotal); + + ACE_NEW(dstbuf, char[stotal]); + if (dstbuf == 0) + { + memabort (); + } + zero(dstbuf, stotal); + } + + if (use_array) + { + // We want to test all the possible loop unrolling deltas. + int t; + for (t = total - 3; t <= total; t++) + { + int delta; + if (sizeof(long) <= H::size ()) + { + delta = 1; + } + else + { + delta = (int) (sizeof(long) / H::size ()); + } + + // We want to test all the posible source/destination buffer + // alignment combinations. + int sk; + for (sk = 0; sk < delta; sk++) + { + int dk; + for (dk = 0; dk < delta; dk++) + { + int tdelta = t - mymax(sk, dk); + + CDR_Test<T, H>::do_test(tdelta, niter, 1, + srcbuf, dstbuf, + sk, dk); + + } + } + } + } + else + { + do_test(total, niter, use_array, srcbuf, dstbuf); + } + + delete[] srcbuf; + delete[] dstbuf; +} + +// Generate an ``interesting'' value for testing at pos >i<. +template<class T, class H> T +CDR_Test<T, H>::checkval (int i) +{ + if (!H::integral ()) + { + // If T is not an integral type, we don't want to risk + // getting an invalid bit pattern for a T value. + return T(i); + } + else + { + T v; + unsigned char* s = reinterpret_cast<unsigned char*> ((&v)); + unsigned int j; + for (j = 0; j < H::size (); j++) + { + s[j] = (unsigned char) ((j + i * H::size ()) % 256); + } + + return v; + } +} + +// +// Returns in s an hex representation of T's memory. +// (differences in byte order will be noticed in s). +// +// If T = int, +// t = 0xaabbccdd, +// => s = "aabbccdd" for big endian machines, +// s = "ddccbbaa" for little endian machines. +// +template<class T, class H> void +CDR_Test<T, H>::ttoh (const T& t, char* s) +{ + const unsigned char *const p = + reinterpret_cast<const unsigned char*> (&t); + + static char digits[16] = { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' + }; + + const unsigned char* q; + for (q = p; q < p + H::size (); ++q) + { + int k = *q; + *s++ = digits[ k >> 4 ]; + *s++ = digits[ k & 15 ]; + } + + *s = 0; +} + +void +do_seal (char* pos) +{ + char* ps = reinterpret_cast<char*> (&seal); + pos[0] = ps[0]; + pos[1] = ps[1]; + pos[2] = ps[2]; + pos[3] = ps[3]; +} + +int +check_seal (char* pos) +{ + char* ps = reinterpret_cast<char*> (&seal); + return (pos[0] == ps[0] + && pos[1] == ps[1] + && pos[2] == ps[2] + && pos[3] == ps[3]); +} + +// +// returns the alignment of ptr, wrt ACE_CDR::MAX_ALIGNMENT. +// +int +tellalign (const char* const ptr) +{ + int align = ACE_CDR::MAX_ALIGNMENT; + while (ptr != ACE_ptr_align_binary(ptr, align)) + { + align = align >> 1; + } + + return align; +} + +template<class T, class H> void +CDR_Test<T, H>::do_test (int total, int niter, int use_array, + char* srcbuf, char* dstbuf, + int src_offset, int dst_offset) +{ + if (!use_array) + { + dst_offset = src_offset = 0; + } + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT( "Starting Test for %s: %d elements " ) + ACE_TEXT( "%susing arrays.\n" ), + H::name (), + total, + ((use_array) ? ACE_TEXT( "" ) : ACE_TEXT( "not " )))); + + + if (!use_array && (total % 4) != 0) + { + int lasttotal = total; + total -= (total % 4); + ACE_DEBUG((LM_DEBUG, + ACE_TEXT( "Rounding from %d to %d elements.\n" ), + lasttotal, + total)); + } + + char* src = ACE_ptr_align_binary(srcbuf, H::size ()); + T* idata = reinterpret_cast<T*> (src); + idata += src_offset; + src = reinterpret_cast<char*> (idata); + + { + int i; + for (i = 0; i < total; i++) + { + idata[i] = CDR_Test<T, H>::checkval (i); + } + } + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT( "Writing data...\n" ))); + + char* toread = 0; + { + ACE_ASSERT(use_array || total % 4 == 0); + + double totalsecs = 0.0; + int n; + for (n = 0; n < niter; n++) + { + size_t size = H::size () * (dst_offset + total) + + ACE_CDR::MAX_ALIGNMENT; + ACE_OutputCDR os (dstbuf, size); + + // This is intrusive... + char* const end = os.begin ()->wr_ptr() + size; + + do_seal (end); + + double secs = 0.0; + if (use_array) + { + { + int i; + for (i = 0; i < dst_offset; i++) + { + os << T(0); + } + } + + if (n == 0) + { + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("* src align = %d, dst align = %d\n"), + tellalign (src), + tellalign (os.begin ()->wr_ptr ()))); + } + + Crono crono; + crono.start (); + H::write_array (os, idata, total); + crono.stop (); + secs = crono.read_seconds (); + } + else + { + int i = 0; + for (; i < dst_offset; i++) + { + os << T(0); + } + i = 0; + + Crono crono; + crono.start(); + while (i < total) + { + os << idata[i++]; + os << idata[i++]; + os << idata[i++]; + os << idata[i++]; + // static char rs[32 + 1]; + // CDR_Test<T,H>::ttoh (idata[i], rs); + // ACE_DEBUG ((LM_DEBUG, "Write idata[%d] = %s\n", i, rs)); + // os << idata[i]; + // i++; + } + crono.stop (); + secs = crono.read_seconds (); + } + + if (!check_seal(end)) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT( "Broken seal, aborting.\n" ))); + ACE_OS::exit(1); + } + + totalsecs += secs; + + if (n == niter - 1) + { + toread = os.begin ()->rd_ptr (); + } + } + + totalsecs = totalsecs / niter; + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("Writing to stream %d %s values: %f seconds.\n"), + total, + H::name (), + totalsecs)); + } + + { + int i; + for (i = 0; i < total; i++) + { + idata[i] = 0; + } + } + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT( "Reading them back in opposing byte order...\n" ))); + + const int opposite_byte_order = 1 - ACE_CDR_BYTE_ORDER; + + { + double totalsecs = 0.0; + int n; + for (n = 0; n < niter; n++) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("====== Read iteration %d\n"), n)); + + size_t size = (total + dst_offset) * H::size (); + ACE_InputCDR is (toread, size, opposite_byte_order); + + // This is intrusive... + char* const end = is.rd_ptr () + size; + + do_seal (end); + + double secs = 0.0; + if (use_array) + { + { + int i; + for (i = 0; i < dst_offset; i++) + { + T v; + is >> v; + } + } + + if (n == 0) + { + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("* src align = %d, dst align = %d\n"), + tellalign (is.rd_ptr ()), + tellalign (src))); + } + + Crono crono; + crono.start (); + H::read_array (is, idata, total); + crono.stop (); + secs = crono.read_seconds (); + + // Testing for good bit value. Try reading atleast 10 + // times the size of total. It should fail with good bit + // set to 0. + H::read_array (is, idata, 10 * total); + + if (is.good_bit () != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test for good bit failed in %s Array_test \n"), + H::name ())); + } + } + else + { + int i = 0; + Crono crono; + crono.start (); + while (i < total) + { +#if 0 + T v; + is >> v; + static char rs[32 + 1]; + CDR_Test<T,H>::ttoh (v, rs); + ACE_DEBUG ((LM_DEBUG, "Read idata[%d] = %s\n", i, rs)); + idata[i] = v; + i++; +#else + is >> idata[i++]; + is >> idata[i++]; + is >> idata[i++]; + is >> idata[i++]; +#endif /* 0 */ + } + crono.stop (); + secs = crono.read_seconds (); + } + totalsecs += secs; + + if (!check_seal (end)) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT( "Broken seal, aborting.\n" ))); + ACE_OS::exit(1); + } + } + + totalsecs = totalsecs / niter; + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("Reading from stream %d %s values") + ACE_TEXT (" (byte swapping): %f seconds.\n"), + total, + H::name (), + totalsecs)); + } + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("Now checking data...\n") )); + + int errors = 0; + const int maxerrors = 6; + + { + int i; + for (i = 0; i < total; i++) + { + T rv; + + const char* src = reinterpret_cast<const char*> ((idata + i)); + char* dst = reinterpret_cast<char*> ((&rv)); + + H::swap(src, dst); + + // Due to a "feature" of the gcc 4.1.1 optimizer, we need to do + // something with the src pointer so that it doesn't optimize it + // away. Calling tellalign() is benign, but the optimizer + // doesn't know/care. -- Chad Elliott 1/10/2007 + tellalign (src); + + T cv = CDR_Test<T, H>::checkval (i); + if (rv != cv) + { + static char rs[32 + 1]; + static char cs[32 + 1]; + CDR_Test<T, H>::ttoh (rv, rs); + CDR_Test<T, H>::ttoh (cv, cs); + ACE_ERROR((LM_ERROR, + ACE_TEXT ( "Wrong value at pos %d:" ) + ACE_TEXT ( " '%s' should be '%s'.\n" ), + i, rs, cs)); + errors++; + if (errors == maxerrors) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT ( "%d errors found, ") + ACE_TEXT ( "interrupting check.\n" ), + errors)); + break; + } + } + } + } + + if (errors != 0) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT (" assertion failed: Inconsistencies found (%d), ") + ACE_TEXT ("aborting.\n"), errors)); + ACE_OS::exit(1); + } + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("Data OK, test %s completed.\n"), + H::name ())); +} + +template <class T, class N> +CDR_Test<T, N>::~CDR_Test () +{ +} + +// +// Helper Clases for the second template parameter of CDR_Test. +// One for each tested CDR type. +// + +struct DoubleHelper +{ + static const ACE_TCHAR* name () + { + return ACE_TEXT ("CDR::Double"); + } + static int integral () + { + return 0; + } + static void read_array (ACE_InputCDR& is, + ACE_CDR::Double* x, + ACE_UINT32 n) + { + is.read_double_array (x, n); + } + static void write_array (ACE_OutputCDR& os, + ACE_CDR::Double* x, + ACE_UINT32 n) + { + os.write_double_array (x, n); + } + static void swap (const char *src, char *dst) + { + ACE_CDR::swap_8 (src, dst); + } + static size_t size () + { + return sizeof(ACE_CDR::Double); + } +}; + +struct FloatHelper +{ + static const ACE_TCHAR* name () + { + return ACE_TEXT ("CDR::Float"); + } + static int integral () + { + return 0; + } + static void read_array (ACE_InputCDR& is, + ACE_CDR::Float* x, + ACE_UINT32 n) + { + is.read_float_array (x, n); + } + static void write_array (ACE_OutputCDR& os, + ACE_CDR::Float* x, + ACE_UINT32 n) + { + os.write_float_array (x, n); + } + static void swap (const char *src, char *dst) + { + ACE_CDR::swap_4 (src, dst); + } + static size_t size () + { + return sizeof(ACE_CDR::Float); + } +}; + +struct ShortHelper +{ + static const ACE_TCHAR* name () + { + return ACE_TEXT ("CDR::Short"); + } + static int integral () + { + return 1; + } + static void read_array (ACE_InputCDR& is, + ACE_CDR::Short* x, + ACE_UINT32 n) + { + is.read_short_array (x, n); + } + static void write_array (ACE_OutputCDR& os, + ACE_CDR::Short* x, + ACE_UINT32 n) + { + os.write_short_array (x, n); + } + static void swap (const char *src, char *dst) + { + ACE_CDR::swap_2 (src, dst); + } + static size_t size () + { + return sizeof(ACE_CDR::Short); + } +}; + +struct LongHelper +{ + static const ACE_TCHAR* name () + { + return ACE_TEXT ("CDR::Long"); + } + static int integral () + { + return 1; + } + static void read_array (ACE_InputCDR& is, + ACE_CDR::Long* x, + ACE_UINT32 n) + { + is.read_long_array (x, n); + } + static void write_array (ACE_OutputCDR& os, + ACE_CDR::Long* x, + ACE_UINT32 n) + { + os.write_long_array (x, n); + } + static void swap (const char *src, char *dst) + { + ACE_CDR::swap_4 (src, dst); + } + static size_t size () + { + return sizeof(ACE_CDR::Long); + } +}; + +#if !defined (ACE_LACKS_LONGLONG_T) +struct LongLongHelper +{ + static const ACE_TCHAR* name () + { + return ACE_TEXT ("CDR::LongLong"); + } + static int integral () + { + return 1; + } + static void read_array (ACE_InputCDR& is, + ACE_CDR::LongLong* x, + ACE_UINT32 n) + { + is.read_longlong_array (x, n); + } + static void write_array (ACE_OutputCDR& os, + ACE_CDR::LongLong* x, + ACE_UINT32 n) + { + os.write_longlong_array (x, n); + } + + static void swap (const char *src, char *dst) + { + ACE_CDR::swap_8 (src, dst); + } + static size_t size () + { + return sizeof(ACE_CDR::LongLong); + } +}; +#endif /* ! ACE_LACKS_LONGLONG_T */ + +struct CharHelper +{ + static const ACE_TCHAR* name () + { + return ACE_TEXT ("CDR::Char"); + } + static int integral () + { + return 1; + } + static void read_array (ACE_InputCDR& is, + ACE_CDR::Char* x, + ACE_UINT32 n) + { + is.read_char_array (x, n); + } + static void write_array (ACE_OutputCDR& os, + ACE_CDR::Char* x, + ACE_UINT32 n) + { + os.write_char_array (x, n); + } + static void swap (const char *src, char *dst) + { + *dst = *src; + } + static size_t size () + { + return sizeof(ACE_CDR::Char); + } +}; + +void usage (const ACE_TCHAR* cmd) +{ + ACE_ERROR((LM_ERROR, + ACE_TEXT ("Usage: %s ") + ACE_TEXT ("[-n n] " ) + ACE_TEXT ("[[-d n|-f n|-q n|-w n|-h n|-c n|-t n] | [-t n]]\n") + ACE_TEXT (" -n n: average time for n iterations.\n") + ACE_TEXT (" -d n: n double precision floating point\n") + ACE_TEXT (" -f n: n single precision floating point\n") + ACE_TEXT (" -q n: n quadwords (int64).\n") + ACE_TEXT (" -w n: n words (int32).\n") + ACE_TEXT (" -h n: n halfwords (int16).\n") + ACE_TEXT (" -c n: n chars.\n") + ACE_TEXT (" -t n: n iterations for every type.\n") + ACE_TEXT (" n must be >= 16 for dfqwhct.\n") + ACE_TEXT (" If you provide one of dfqwhc, then only the\n") + ACE_TEXT (" test for the corresponding type ") + ACE_TEXT ("will be performed.\n"), + cmd)); + ACE_OS::exit(1); +} + +int +validtotal (int n) +{ + return n >= 16; +} + +int +validiters (int n) +{ + return n > 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("CDR_Array_Test")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This is ACE Version %u.%u.%u\n\n"), + ACE::major_version (), + ACE::minor_version(), + ACE::beta_version())); + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("d:f:q:w:h:c:t:n:")); + int dtotal = 0; + int ftotal = 0; + int qtotal = 0; + int wtotal = 0; + int htotal = 0; + int ctotal = 0; + int total = 0; + int niter = 0; + + struct { int c; int *v; int (*checkf)(int); } opts[] = { + { 'd', &dtotal, validtotal }, + { 'f', &ftotal, validtotal }, + { 'q', &qtotal, validtotal }, + { 'w', &wtotal, validtotal }, + { 'h', &htotal, validtotal }, + { 'c', &ctotal, validtotal }, + { 't', &total, validtotal }, + { 'n', &niter, validiters }, + }; + + int n = sizeof(opts)/sizeof(opts[0]); + + int opt; + while ((opt = get_opt ()) != EOF) + { + int got = 0; + int i; + for (i = 0; i < n; i++) + { + if (opts[i].c == opt) + { + int v = ACE_OS::atoi (get_opt.opt_arg ()); + if (!(opts[i].checkf) (v)) + { + usage(ACE_TEXT("CDR_Array_Test")); + } + + *(opts[i].v) = v; + got = 1; + break; + } + } + + if (!got) + { + usage(ACE_TEXT("CDR_Array_Test")); + } + } + + if (total == 0) + { + total = default_total; + } + + if (niter == 0) + { + niter = default_niter; + } + + if (dtotal == 0 + && ftotal == 0 + && qtotal == 0 + && wtotal == 0 + && htotal == 0 + && ctotal == 0) + { + dtotal = ftotal = qtotal = wtotal = htotal = ctotal = total; + } + + int use_array; + for (use_array = 0; use_array < 2; use_array++) + { + { + CDR_Test<ACE_CDR::Double, DoubleHelper> + test (dtotal, niter, use_array); + } + { + CDR_Test<ACE_CDR::Float, FloatHelper> + test (ftotal, niter, use_array); + } +#if !defined (ACE_LACKS_LONGLONG_T) + { + CDR_Test<ACE_CDR::LongLong, LongLongHelper> + test (qtotal, niter, use_array); + } +#endif /* ! ACE_LACKS_LONGLONG_T */ + { + CDR_Test<ACE_CDR::Long, LongHelper> + test (wtotal, niter, use_array); + } + { + CDR_Test<ACE_CDR::Short, ShortHelper> + test (htotal, niter, use_array); + } + { + CDR_Test<ACE_CDR::Char, CharHelper> + test (ctotal, niter, use_array); + } + } + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/CDR_File_Test.cpp b/ACE/tests/CDR_File_Test.cpp new file mode 100644 index 00000000000..30ca4d8b636 --- /dev/null +++ b/ACE/tests/CDR_File_Test.cpp @@ -0,0 +1,486 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// CDR_File_Test.cpp +// +// = DESCRIPTION +// Checks the functionality of the ACE CDR streams used for file +// I/O. +// +// = AUTHORS +// Giga Giguashvili <gregoryg@ParadigmGeo.com> and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_Memory.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_string.h" +#include "ace/CDR_Stream.h" +#include "ace/FILE_Connector.h" +#include "ace/Auto_Ptr.h" +#include "ace/Get_Opt.h" +#include "ace/ACE.h" +#include "ace/Truncate.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +ACE_RCSID(tests, CDR_File_Test, "$Id$") + +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) | defined (ACE_HAS_WINCE) + +#if defined (ACE_HAS_WINCE) +#include "CE_fostream.h" +#endif // ACE_HAS_WINCE + +class CDR_Test +{ + // = TITLE + // Simple class that's used to read and write CDR streams. + + friend ostream& operator << (ostream &os, const CDR_Test &t); + // Output the state of a <CDR_Test> object to the <ostream>. + + friend void operator << (ACE_OutputCDR &os, const CDR_Test &t); + // Convert the state of this object into an <ACE_OutputCDR>. + + friend void operator >> (ACE_InputCDR &is, CDR_Test &); + // Convert the <ACE_InputCDR> into the state of this object. + +public: + CDR_Test (void); + // Default constructor. + + CDR_Test (ACE_CDR::Char o, + ACE_CDR::Short s, + ACE_CDR::Long w, + ACE_CDR::ULongLong lw, + ACE_CDR::Float f, + ACE_CDR::Double d); + // Constructor. + + bool operator == (const CDR_Test &rhs) const; + // Compare <rhs> for equality with <this>. + +private: + ACE_CDR::Char char_; + ACE_CDR::Short word2_; + ACE_CDR::Long word4_; + ACE_CDR::ULongLong word8_; + ACE_CDR::Float fpoint_; + ACE_CDR::Double dprec_; +}; + +ostream & +operator << (ostream &os, + const CDR_Test &t) +{ +#if defined (ACE_OPENVMS) + // to circumvent some obscure bug with OpenVMS iostreams digit conversions + // combined with shared libraries???? + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n" + "Char: %c\n" + "Short: %u\n" + "Long: %d\n"), + t.char_, + t.word2_, + t.word4_)); + + ACE_CDR::ULongLong hi = (t.word8_ >> 32); + ACE_CDR::ULongLong lo = (t.word8_ & 0xffffffff); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n" + "ULongLong 1st half: %x\n" + "ULongLong 2nd half: %x\n" + "Float: %f\n" + "Double: %f\n"), + ACE_Utils::truncate_cast<ACE_UINT32> (hi), + ACE_Utils::truncate_cast<ACE_UINT32> (lo), + t.fpoint_, + t.dprec_)); +#else + os << "Char: " << t.char_ << endl + << "Short: " << t.word2_ << endl + << "Long: " << t.word4_ << endl; + + ACE_CDR::ULongLong hi = (t.word8_ >> 32); + ACE_CDR::ULongLong lo = (t.word8_ & 0xffffffff); + + os << "ULongLong 1st half: " + << hex + << ACE_Utils::truncate_cast<ACE_UINT32> (hi) + << dec << endl + << "ULongLong 2nd half: " + << hex + << ACE_Utils::truncate_cast<ACE_UINT32> (lo) + << dec << endl + << "Float: " << t.fpoint_ << endl + << "Double: " << t.dprec_ << endl; +#endif + return os; +} + +CDR_Test::CDR_Test (void) + : char_ (0), + word2_ (0), + word4_ (0), + word8_ (0), + fpoint_ (0.0), + dprec_ (0.0) +{ +} + +CDR_Test::CDR_Test (ACE_CDR::Char o, + ACE_CDR::Short s, + ACE_CDR::Long w, + ACE_CDR::ULongLong lw, + ACE_CDR::Float f, + ACE_CDR::Double d) + : char_ (o), + word2_ (s), + word4_ (w), + word8_ (lw), + fpoint_ (f), + dprec_ (d) +{ +} + +void +operator << (ACE_OutputCDR &os, const CDR_Test &t) +{ + os << t.char_; + os << t.word2_; + os << t.word4_; + os << t.word8_; + os << t.fpoint_; + os << t.dprec_; +} + +void +operator >> (ACE_InputCDR &is, CDR_Test &t) +{ + is >> t.char_; + is >> t.word2_; + is >> t.word4_; + is >> t.word8_; + is >> t.fpoint_; + is >> t.dprec_; +} + +bool +CDR_Test::operator == (const CDR_Test &rhs) const +{ + // @@ Workaround bug in egcs-1.1.1 using a single && expression + // results in UMR errors in purify. + if (this->char_ != rhs.char_) + return false; + if (this->word2_ != rhs.word2_) + return false; + if (this->word4_ != rhs.word4_) + return false; + if (this->word8_ != rhs.word8_) + return false; + if (this->fpoint_ != rhs.fpoint_) + return false; + if (this->dprec_ != rhs.dprec_) + return false; + return true; +} + +static int +run_test (int write_file, + ACE_FILE_IO &file, + const ACE_TCHAR *filename, + CDR_Test &cdr_test) +{ + if (write_file) + { + char byte_order = ACE_CDR_BYTE_ORDER; + size_t n = file.send (&byte_order, 1); + if (n != 1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("send failed on %p\n"), + filename), + -1); + + ACE_OutputCDR output_cdr (0, + ACE_CDR_BYTE_ORDER, + 0, + 0, + 0, + ACE_DEFAULT_CDR_MEMCPY_TRADEOFF, + ACE_CDR_GIOP_MAJOR_VERSION, + ACE_CDR_GIOP_MINOR_VERSION); + // Marshal the <CDR_Test> object data to the output CDR stream. + output_cdr << cdr_test; + + // Output the data to cout. +#if defined (ACE_HAS_WINCE) && defined (ACE_LACKS_IOSTREAM_TOTALLY) + // Since CE does not have ostream, ace_file_stream and output_file() cannot + // be used. Just use 'hard-coded' file name here. + (*ACE_CE_OSTREAM::instance()).open(ACE_TEXT("\\Log\\CDR_File_Test.txt")); + (*ACE_CE_OSTREAM::instance()) << cdr_test; +#else + *ace_file_stream::instance ()->output_file () << cdr_test; +#endif // ACE_HAS_WINCE + + // Save the data. + const ACE_Message_Block *output_mb = + output_cdr.begin (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writing file %s in %s endian format...\n"), + filename, + ACE_CDR_BYTE_ORDER ? ACE_TEXT("little") : ACE_TEXT("big"))); + + n = file.send (output_mb->rd_ptr (), + output_mb->length ()); + if (n != (size_t) output_mb->length ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("send failed on %p\n"), + filename), + -1); + } + else // We're reading from the file. + { + ACE_FILE_Info info; + if (file.get_info (info) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("get_info failed on %p\n"), + filename), + -1); + + ACE_OFF_T msgsize = info.size_ - 1; + + // Allocate the input buffer + char *buffer = 0; + ACE_NEW_RETURN (buffer, + char[msgsize], + -1); +#if defined (ACE_INITIALIZE_MEMORY_BEFORE_USE) + ACE_OS::memset(buffer, 0, sizeof (buffer)); +#endif /* ACE_INITIALIZE_MEMORY_BEFORE_USE */ + + // Make sure <buffer> is released automagically. + ACE_Auto_Basic_Array_Ptr<char> b (buffer); + + // Move the file pointer back to the beginning of the file. + if (file.seek (0, + SEEK_SET) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + filename), + -1); + + char byte_order; + ssize_t size = file.recv (&byte_order, 1); + if (size != 1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Read %d bytes, rather than expected ") + ACE_TEXT ("1 bytes\n"), + size), + -1); + + // Read the cdr data from the file into the buffer. + size = file.recv (buffer, msgsize); + if (size != msgsize) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Read %d bytes, rather than expected ") + ACE_TEXT ("%d bytes\n"), + size, + msgsize), + -1); + + // Create message block for the whole file. Ensure that it is + // aligned to properly handle the double. + ACE_Message_Block mb (ACE_CDR::MAX_ALIGNMENT + msgsize); + ACE_CDR::mb_align (&mb); + + mb.copy (buffer, msgsize); + + // Create CDR input stream from the message block. + + ACE_InputCDR input_cdr (&mb); + input_cdr.reset_byte_order ((int) byte_order); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Reading file %s in %s endian format...\n"), + filename, + ACE_CDR_BYTE_ORDER ? ACE_TEXT("little") : ACE_TEXT("big"))); + + CDR_Test temp; + + // Demarshal the data from the input CDR stream into the + // <CDR_Test> object. + input_cdr >> temp; + +#if defined (ACE_HAS_WINCE) && defined (ACE_LACKS_IOSTREAM_TOTALLY) + (*ACE_CE_OSTREAM::instance()) << temp; +#else + *ace_file_stream::instance ()->output_file () << temp; +#endif // ACE_HAS_WINCE + + if (!(temp == cdr_test)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Data mismatch across file\n"))); + } + + return 0; +} + +static void +usage (const ACE_TCHAR *cmd) +{ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Usage: %s ") + ACE_TEXT ("[-f filename [-w|-r]]"), + cmd)); + ACE_OS::exit (1); +} + +// Main function + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("CDR_File_Test")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This is ACE Version %u.%u.%u\n\n"), + ACE::major_version (), + ACE::minor_version (), + ACE::beta_version ())); + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("f:rw")); + int opt; + int reading = 1; + int writing = 1; + ACE_TCHAR* fn = 0; + while ((opt = get_opt ()) != EOF) + { + switch (opt) + { + case 'f': + fn = get_opt.opt_arg (); + break; + case 'r': + writing = 0; + break; + case 'w': + reading = 0; + break; + case '?': + default: + usage (ACE_TEXT("CDR_File_Test")); + } + } + + if ((!reading || !writing) && fn == 0) + usage (ACE_TEXT("CDR_File_Test")); + + if (!reading && !writing) + usage (ACE_TEXT("CDR_File_Test")); + + // Create a temporary filename. + ACE_FILE_Addr filename (ACE_sap_any_cast (ACE_FILE_Addr &)); + if (fn != 0) + filename.set (fn); + + + ACE_FILE_Connector connector; + ACE_FILE_IO file; + + // Open up the file. + if (connector.connect (file, + filename, + 0, + ACE_Addr::sap_any, + 0, + ((writing) ? (O_RDWR | O_CREAT) : O_RDONLY), + ACE_DEFAULT_FILE_PERMS) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("connect failed for %p\n"), + filename.get_path_name ()), + 1); + +#if !defined (ACE_VXWORKS) && !defined (ACE_HAS_PHARLAP) || (defined(ACE_VXWORKS) && (ACE_VXWORKS >= 0x640)) +# define TEST_CAN_UNLINK_IN_ADVANCE +#endif + +#if defined (TEST_CAN_UNLINK_IN_ADVANCE) + if (fn == 0) + { + // Unlink this file right away so that it is automatically removed + // when the process exits. + if (file.unlink () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("pre-unlink failed for %p\n"), + filename.get_path_name ()), + 1); + } +#endif + + CDR_Test cdr_test ('a', + 0x00ff, + 0xaabbccdd, + 0x01234567, + 1.54321f, + 1.12345); + + if (writing) + { + // write the file. + run_test (1, + file, + filename.get_path_name (), + cdr_test); + } + + if (reading) + { + // read the file. + run_test (0, + file, + filename.get_path_name (), + cdr_test); + } + +#if !defined (TEST_CAN_UNLINK_IN_ADVANCE) + if (fn == 0) + { + file.close (); + if (file.unlink () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unlink failed for %p\n"), + filename.get_path_name ()), + 1); + } +#endif + + ACE_END_TEST; + return 0; +} + +#else /* ! ACE_LACKS_IOSTREAM_TOTALLY */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("CDR_File_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("iostreams not supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} + +#endif /* ! ACE_LACKS_IOSTREAM_TOTALLY */ diff --git a/ACE/tests/CDR_Test.cpp b/ACE/tests/CDR_Test.cpp new file mode 100644 index 00000000000..49bfde79de3 --- /dev/null +++ b/ACE/tests/CDR_Test.cpp @@ -0,0 +1,703 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// CDR_Test.cpp +// +// = DESCRIPTION +// Checks the functionality of the ACE CDR streams. +// +// = AUTHORS +// Istvan Buki <istvan.buki@euronet.be> and +// Jeff Parsons <parsons@cs.wustl.edu> and +// Carlos O'Ryan <coryan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Get_Opt.h" +#include "ace/Auto_Ptr.h" +#include "ace/CDR_Stream.h" +#include "ace/CDR_Size.h" +#include "ace/SString.h" +#include "ace/ACE.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_wchar.h" + +ACE_RCSID(tests, CDR_Test, "$Id$") + +static int n = 4096; +static int nloops = 100; + +struct CDR_Test_Types +{ + CDR_Test_Types (void); + + ACE_CDR::Octet o; + ACE_CDR::Short s; + ACE_CDR::Long l; + const ACE_CDR::Char *str; + const ACE_CDR::WChar *wstr; + ACE_CDR::Double d; + ACE_CDR::Short reps; + ACE_CDR::Long repl; + + int test_put (ACE_OutputCDR& cdr); + int test_get (ACE_InputCDR& cdr) const; + int test_put_placeholder (ACE_OutputCDR& cdr); + int test_get_placeholder (ACE_InputCDR& cdr) const; + + enum + { + ARRAY_SIZE = 10 + }; + + ACE_CDR::Short a[ARRAY_SIZE]; +}; + +CDR_Test_Types::CDR_Test_Types (void) + : o (1), + s (2), + l (4), + str ("abc"), + wstr (0), + d (8), + reps (-123), + repl (-65800L) +{ + for (int i = 0; + i < CDR_Test_Types::ARRAY_SIZE; + ++i) + a[i] = i; +} + +static int +short_stream (void) +{ + // counter + u_int i; + + // Build an output stream + ACE_OutputCDR os; + ACE_SizeCDR ss; + + // Basic types for output + ACE_CDR::Char ch = 'A'; + ACE_CDR::Char wchtmp[] = {'\xF3'}; + ACE_CDR::WChar wch = *wchtmp; + ACE_CDR::WChar wchar2[] = {'\x00'}; // empty wide string + ACE_CDR::WChar *wstr = wchar2; + ACE_CString str ("Test String"); + ACE_CDR::Short s = -123; + ACE_CDR::UShort us = 123; + ACE_CDR::Long l = -65800L; + ACE_CDR::ULong ul = 65800UL; + ACE_CDR::Float f = 1.23f; + ACE_CDR::Double d = 123.456789; + + // Arrays for output + ACE_CDR::Short s_array[3] = { -1, 0, 1 }; + ACE_CDR::Long l_array[3] = { -345678, 0, 345678 }; + ACE_CDR::Float f_array[3] = { -1.23f, 0.0f, 1.23f }; + ACE_CDR::Double d_array[3] = { -123.456789, 0.0, 123.456789 }; + + ACE_OutputCDR::from_char fc (ch); + ACE_OutputCDR::from_wchar fwc (wch); + os << fc; + os << fwc; + os << str; + os << wstr; + os << s; + os << us; + os << l; + os << ul; + os << f; + os << d; + os.write_short_array (s_array, 3); + os.write_long_array (l_array, 3); + os.write_float_array (f_array, 3); + os.write_double_array (d_array, 3); + + // Do the same for size stream. + ss << fc; + ss << fwc; + ss << str; + ss << wstr; + ss << s; + ss << us; + ss << l; + ss << ul; + ss << f; + ss << d; + ss.write_short_array (s_array, 3); + ss.write_long_array (l_array, 3); + ss.write_float_array (f_array, 3); + ss.write_double_array (d_array, 3); + + // Check the size. + if (ss.total_length () != os.total_length ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("representation length does not match")), + 1); + + const ACE_Message_Block *out_mb = os.begin (); + size_t len = out_mb->length (); + + // Create an input stream (copy constructor) + ACE_InputCDR is (os); + const ACE_Message_Block *in_mb = is.start (); + + if (in_mb->length () != len) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("buffer length not preserved")), + 1); + + for (i = 0; i < len; i++) + { + unsigned long const in_chunk = + static_cast<unsigned long> (* (in_mb->rd_ptr () + i)); + + unsigned long const out_chunk = + static_cast<unsigned long> (* (out_mb->rd_ptr () + i)); + + if (in_chunk != out_chunk) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("buffer contents not preserved")), + 1); + } + + // Basic types for input + ACE_CDR::Char ch1 = '\0'; + ACE_CDR::WChar wch1 = '\x00'; + ACE_CDR::WChar *wstr1 = 0; + ACE_CString str1; + ACE_CDR::Short s1 = 0; + ACE_CDR::UShort us1 = 0; + ACE_CDR::Long l1 = 0L; + ACE_CDR::ULong ul1 = 0UL; + ACE_CDR::Float f1 = 0.0f; + ACE_CDR::Double d1 = 0.0; + + // Arrays for input + ACE_CDR::Short s_array1[3]; + ACE_CDR::Long l_array1[3]; + ACE_CDR::Float f_array1[3]; + ACE_CDR::Double d_array1[3]; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Checking operators and arrays\n\n"))); + + ACE_InputCDR::to_char tc (ch1); + is >> tc; + ACE_InputCDR::to_wchar twc (wch1); + is >> twc; + is >> str1; + ACE_InputCDR::to_wstring twstr (wstr1, 0); + is >> twstr; + // @todo Lose the ACE_Auto_Array_Ptr. We should be using a + // std::string, or the like. + ACE_Auto_Array_Ptr<ACE_CDR::WChar> safe_wstr (wstr1); + is >> s1; + is >> us1; + is >> l1; + is >> ul1; + is >> f1; + is >> d1; + is.read_short_array (s_array1, 3); + is.read_long_array (l_array1, 3); + is.read_float_array (f_array1, 3); + is.read_double_array (d_array1, 3); + + if (ch1 != ch) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("char transfer error")), + 1); + + if (wch1 != wch) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("wchar transfer error")), + 1); + + if (str1 != str) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("string transfer error")), + 1); + + if (ACE_OS::wscmp (wstr1, wstr)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("wide string transfer error")), + 1); + + + if (s1 != s) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("short transfer error")), + 1); + + if (us1 != us) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ushort transfer error")), + 1); + + if (l1 != l) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("long transfer error")), + 1); + + if (ul1 != ul) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ulong transfer error")), + 1); + + if (f1 != f) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("float transfer error")), + 1); + + if (d1 != d) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("double transfer error")), + 1); + + for (i = 0 ; i < 3; i++) + if (s_array1[i] != s_array[i]) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("short array transfer error")), + 1); + + for (i = 0 ; i < 3; i++) + if (l_array1[i] != l_array[i]) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("long array transfer error")), + 1); + + for (i = 0 ; i < 3; i++) + if (f_array1[i] != f_array[i]) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("float array transfer error")), + 1); + + for (i = 0 ; i < 3; i++) + if (d_array1[i] != d_array[i]) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("double array transfer error")), + 1); + + return 0; +} + +int +CDR_Test_Types::test_put (ACE_OutputCDR &cdr) +{ + for (int i = 0; i < n; ++i) + { + if (cdr.write_octet (this->o) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_octet[%d] failed\n"), + i), + 1); + if (cdr.write_short (this->s) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_short[%d] failed\n"), + i), + 1); + if (cdr.write_octet (this->o) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_octet-2[%d] failed\n"), + i), + 1); + if (cdr.write_long (this->l) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_long[%d] failed\n"), + i), + 1); + if (cdr.write_long (this->l) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_long-2[%d] failed\n"), + i), + 1); + + if (cdr.write_string (this->str) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_string[%d] failed\n"), + i), + 1); + + if (cdr.write_wstring (this->wstr) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write_wstring[%d] failed\n"), + i), + 1); + + } + + return 0; +} + +int +CDR_Test_Types::test_get (ACE_InputCDR &cdr) const +{ + ACE_CDR::Octet xo; + ACE_CDR::Short xs; + ACE_CDR::Long xl; + + for (int i = 0; i < n; ++i) + { + if (cdr.read_octet (xo) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_octet[%d] failed\n"), + i), + 1); + if (xo != this->o) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("octet[%d] differs\n"), + i), + 1); + if (cdr.read_short (xs) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_short[%d] failed\n"), + i), 1); + if (xs != this->s) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("short[%d] differs\n"), + i), + 1); + if (cdr.read_octet (xo) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_octet-2[%d] failed\n"), + i), + 1); + if (xo != this->o) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("octet-2[%d] differs\n"), + i), + 1); + if (cdr.read_long (xl) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_long[%d] failed\n"), + i), + 1); + if (xl != this->l) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("long[%d] differs\n"), + i), + 1); + if (cdr.read_long (xl) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_long-2[%d] failed\n"), + i), + 1); + if (xl != this->l) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("long-2[%d] differs\n"), + i), + 1); + + ACE_CDR::Char *xstr; + if (cdr.read_string (xstr) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_string2[%d] failed\n"), + i), + 1); + ACE_Auto_Basic_Array_Ptr<ACE_CDR::Char> auto_xstr (xstr); + if (ACE_OS::strcmp (auto_xstr.get (), this->str) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("string[%d] differs\n"), + i), + 1); + + ACE_CDR::WChar *wstr1; + if (cdr.read_wstring (wstr1) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_wstring2[%d] failed\n"), + i), + 1); + // zero length + ACE_Auto_Basic_Array_Ptr<ACE_CDR::WChar> auto_xwstr (wstr1); + if (ACE_OS::wslen(auto_xwstr.get () )) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("wstring[%d] differs\n"), + i), + 1); + } + return 0; +} + +int +CDR_Test_Types::test_put_placeholder (ACE_OutputCDR &cdr) +{ + // Write a placeholder then a bunch of other stuff, then replace. + char *pos = cdr.write_long_placeholder (); + if (test_put (cdr) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("test_put (long placeholder) failed\n")), + 1); + if (!cdr.replace (this->repl, pos)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("replace(long) failed\n")), + 1); + + pos = cdr.write_short_placeholder (); + if (test_put (cdr) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("test_put (short placeholder) failed\n")), + 1); + if (!cdr.replace (this->reps, pos)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("replace(short) failed\n")), + 1); + + return 0; +} + +int +CDR_Test_Types::test_get_placeholder (ACE_InputCDR &cdr) const +{ + ACE_CDR::Short xs; + ACE_CDR::Long xl; + + if (cdr.read_long (xl) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_long failed\n")), + 1); + if (xl != this->repl) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("replaced long differs\n")), + 1); + + // The bunch of stuff written after the placeholder by test_put_placeholder + // should still be valid; check that it is. + if (test_get (cdr) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("test_get (long) failed\n")), + 1); + + if (cdr.read_short (xs) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("read_short failed\n")), 1); + if (xs != this->reps) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("replaced short differs\n")), + 1); + + if (test_get (cdr) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("test_get (short) failed\n")), + 1); + + return 0; +} + + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("CDR_Test")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This is ACE Version %u.%u.%u\n\n"), + ACE::major_version (), + ACE::minor_version(), + ACE::beta_version())); + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("dn:l:")); + int opt; + int debug = 0; + + while ((opt = get_opt ()) != EOF) + { + switch (opt) + { + case 'd': + debug++; + break; + case 'n': + n = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'l': + nloops = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case '?': + default: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Usage: %s ") + ACE_TEXT ("-d debug") + ACE_TEXT ("-n <num> ") + ACE_TEXT ("-l <loops> ") + ACE_TEXT ("\n"), + ACE_TEXT ("CDR_Test"))); + return -1; + } + } + + // Sanity checks. + ACE_ASSERT (sizeof (ACE_CDR::Boolean) >= 1); + ACE_ASSERT (sizeof (ACE_CDR::Octet) == 1); + ACE_ASSERT (sizeof (ACE_CDR::WChar) >= 2); + ACE_ASSERT (sizeof (ACE_CDR::Short) == 2); + ACE_ASSERT (sizeof (ACE_CDR::Long) == 4); + ACE_ASSERT (sizeof (ACE_CDR::LongLong) == 8); + ACE_ASSERT (sizeof (ACE_CDR::Float) == 4); + ACE_ASSERT (sizeof (ACE_CDR::Double) == 8); + ACE_ASSERT (sizeof (ACE_CDR::LongDouble) == 16); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing ACE CDR functions - short stream\n\n"))); + + if (short_stream () != 0 ) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Short stream - no errors\n\n") + ACE_TEXT ("Testing basic types - long stream\n\n"))); + + for (int i = 0; i < nloops; ++i) + { + ACE_OutputCDR output; + CDR_Test_Types test_types; + + if (test_types.test_put (output) != 0) + return 1; + + ACE_InputCDR input (output); + if (debug > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Output CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Input CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + } + + if (test_types.test_get (input) != 0) + return 1; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Long stream - no errors\n\n") + ACE_TEXT ("Testing basic types - long stream[2]\n\n"))); + + for (int j = 0; j < nloops; ++j) + { + ACE_OutputCDR output; + CDR_Test_Types test_types; + + if (test_types.test_put (output) != 0) + return 1; + + ACE_InputCDR input (output.begin ()); + if (debug > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Output CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Input CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + } + + if (test_types.test_get (input) != 0) + return 1; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Long stream[2] - no errors\n\n") + ACE_TEXT ("Testing consolidation\n\n"))); + + ACE_OutputCDR output (30); + CDR_Test_Types test_types; + + if (test_types.test_put (output) != 0) + return 1; + + if (output.begin () == output.end ()) + ACE_DEBUG ((LM_WARNING, + ACE_TEXT ("Only one block needed; test no effective.\n"))); + else + { + if (output.consolidate () != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("consolidate"))); + } + + ACE_InputCDR input (output); + if (debug > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Output CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Input CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + } + + if (test_types.test_get (input) != 0) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Consolidation - no errors\n\n") + ACE_TEXT ("Testing placeholder/replace\n\n"))); + + output.reset(); + if (test_types.test_put_placeholder (output) != 0) + return 1; + + input = ACE_InputCDR(output); + if (debug > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Output CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Input CDR: \n"))); + ACE_HEX_DUMP ((LM_DEBUG, + input.rd_ptr(), + 64)); + } + + if (test_types.test_get_placeholder (input) != 0) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Placeholder/Replace - no errors\n\n"))); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/CE_fostream.cpp b/ACE/tests/CE_fostream.cpp new file mode 100644 index 00000000000..c8c3c606a5f --- /dev/null +++ b/ACE/tests/CE_fostream.cpp @@ -0,0 +1,163 @@ +//============================================================================= +/** + * @file CE_fostream.cpp + * + * $Id$ + * + * @author Si Mong Park <spark@ociweb.com> + * + * This is a helper class to simulate basic functions of fostream for + * Windows CE since WinCE does not have IO stream. + * + * Note that the numeric base conversion does not work and will be displayed + * as received. + */ +// ============================================================================ + +#include "CE_fostream.h" + +// This is CE only, prior to availability of iostreams (VC 8). +#if defined (ACE_HAS_WINCE) && defined (ACE_LACKS_IOSTREAM_TOTALLY) + +ACE_CE_fostream* ACE_CE_fostream::instance_ = 0; + + +ACE_CE_fostream* ACE_CE_fostream::instance (void) +{ + if (instance_ == 0) { + instance_ = new ACE_CE_fostream(); + } + + return instance_; +} + + +ACE_CE_fostream::ACE_CE_fostream() +: ostream_(0) +, displayMode_(dec) +{ +} + + +ACE_CE_fostream::~ACE_CE_fostream() +{ + ACE_OS::fclose(ostream_); +} + + +FILE* ACE_CE_fostream::open(const ACE_TCHAR *prog_name) +{ + ostream_ = ACE_OS::fopen(prog_name, ACE_TEXT("a+")); + + return ostream_; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (NUM_TYPE num_type) +{ + displayMode_ = num_type; + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (const ACE_ANTI_TCHAR* c) +{ + ACE_OS::fprintf(ostream_, "%s", c); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (ACE_ANTI_TCHAR c) +{ + ACE_OS::fprintf(ostream_, "%c", c); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (const ACE_TCHAR* c) +{ + fwprintf(ostream_, ACE_TEXT("%s"), c); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (ACE_TCHAR c) +{ + fwprintf(ostream_, ACE_TEXT("%c"), c); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (short s) +{ + if (displayMode_ == oct) { + const NUM_BITS = ACE_SIZEOF_SHORT * 8; + short currentMax = 16384; + + for (int i = 0; i < NUM_BITS; ++i) { + } + } + else if (displayMode_ == hex) { + } + + fwprintf(ostream_, ACE_TEXT("%d"), s); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (int i) +{ + fwprintf(ostream_, ACE_TEXT("%d"), i); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (unsigned int i) +{ + fwprintf(ostream_, ACE_TEXT("%d"), i); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (long l) +{ + fwprintf(ostream_, ACE_TEXT("%f"), l); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (unsigned long l) +{ + fwprintf(ostream_, ACE_TEXT("%f"), l); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (float f) +{ + fwprintf(ostream_, ACE_TEXT("%f"), f); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (double d) +{ + fwprintf(ostream_, ACE_TEXT("%f"), d); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (long double d) +{ + fwprintf(ostream_, ACE_TEXT("%f"), d); + return *this; +} + + +ACE_CE_fostream& ACE_CE_fostream::operator << (const void* v) +{ + fwprintf(ostream_, ACE_TEXT("%d"), v); + return *this; +} + +#endif /* ACE_HAS_WINCE && ACE_LACKS_IOSTREAMS_TOTALLY */ diff --git a/ACE/tests/CE_fostream.h b/ACE/tests/CE_fostream.h new file mode 100644 index 00000000000..9d84d7fc2be --- /dev/null +++ b/ACE/tests/CE_fostream.h @@ -0,0 +1,105 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file CE_fostream.h + * + * $Id$ + * + * @author Si Mong Park <spark@ociweb.com> + * + * This is a helper class to simulate basic functions of fostream for + * Windows CE since WinCE does not have IO stream. + * + * Note that the numeric base conversion does not work and will be displayed + * as received. + */ +// ============================================================================ + +#ifndef ACE_CE_fostream_h +#define ACE_CE_fostream_h + +#include "ace/config-all.h" + +#if defined (ACE_HAS_WINCE) && defined (ACE_LACKS_IOSTREAM_TOTALLY) + +#include "ace/Null_Mutex.h" +#include "ace/Singleton.h" + +#ifdef ostream +#undef ostream +#endif + +#ifdef OFSTREAM +#undef OFSTREAM +#endif // OFSTREAM + +#define ostream ACE_CE_fostream +#define OFSTREAM ACE_CE_fostream +#define dec ACE_CE_fostream::dec +#define oct ACE_CE_fostream::oct +#define hex ACE_CE_fostream::hex +#define endl ACE_TEXT("\n") + +class ACE_CE_fostream +{ +public: + friend class ACE_Singleton<ACE_CE_fostream, ACE_Null_Mutex>; + + enum NUM_TYPE { + // These numbers are not really meaningful for this class; set same as defined in ios. + // Also, base formatting works only for the integer types. + dec = 0x0010, + oct = 0x0020, + hex = 0x0040 + }; + + static ACE_CE_fostream* instance (void); + + FILE* open(const ACE_TCHAR *prog_name); + + ACE_CE_fostream& operator << (NUM_TYPE); + + ACE_CE_fostream& operator << (unsigned char); + + ACE_CE_fostream& operator << (const ACE_ANTI_TCHAR *); + ACE_CE_fostream& operator << (ACE_ANTI_TCHAR); + + ACE_CE_fostream& operator << (const ACE_TCHAR*); + ACE_CE_fostream& operator << (ACE_TCHAR); + + ACE_CE_fostream& operator << (short); + + ACE_CE_fostream& operator << (int); + ACE_CE_fostream& operator << (unsigned int); + + ACE_CE_fostream& operator << (long); + ACE_CE_fostream& operator << (unsigned long); + ACE_CE_fostream& operator << (float); + ACE_CE_fostream& operator << (double); + ACE_CE_fostream& operator << (long double); + + ACE_CE_fostream& operator << (const void *); + +private: + ACE_CE_fostream(); + + ~ACE_CE_fostream(); + + /** + * The ostream where logging messages can be written. + */ + ACE_OSTREAM_TYPE *ostream_; + + /** + * Currently set numeric base. + */ + NUM_TYPE displayMode_; + + static ACE_CE_fostream* instance_; +}; + +typedef ACE_Singleton<ACE_CE_fostream, ACE_Null_Mutex> ACE_CE_OSTREAM; + +#endif /* ACE_HAS_WINCE && ACE_LACKS_IOSTREAM_TOTALLY */ +#endif /* ACE_CE_fostream_h */ diff --git a/ACE/tests/Cache_Map_Manager_Test.cpp b/ACE/tests/Cache_Map_Manager_Test.cpp new file mode 100644 index 00000000000..400235216e0 --- /dev/null +++ b/ACE/tests/Cache_Map_Manager_Test.cpp @@ -0,0 +1,598 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cache_Map_Manager_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_Cache_Map_Manager> and +// <ACE_Hash_Cache_Map_Manager> that illustrates how to use the +// forward and reverse iterators, as well as the purging and +// caching features. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/OS_NS_string.h" + +#include "ace/OS_Memory.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_time.h" +#include "test_config.h" +#include "ace/Hash_Cache_Map_Manager_T.h" +#include "ace/Map_Manager.h" +#include "ace/Caching_Strategies_T.h" +#include "ace/Functor.h" +#include "ace/Pair_T.h" +#include "ace/Get_Opt.h" +#include "Cache_Map_Manager_Test.h" // Hash_Key class defined in here + +typedef size_t KEY; +typedef size_t VALUE; +typedef int ATTR; +typedef ACE_Pair<VALUE, ATTR> CACHE_VALUE; +typedef ACE_Equal_To<KEY> COMPARE_KEYS; + +typedef ACE_Hash_Map_Manager_Ex<KEY, CACHE_VALUE, Hash_Key, ACE_Equal_To<KEY>, ACE_Null_Mutex> + HASH_MAP_MANAGER; +typedef ACE_Hash_Map_Iterator_Ex<KEY, CACHE_VALUE, Hash_Key, ACE_Equal_To<KEY>, ACE_Null_Mutex> + HASH_MAP_ITERATOR; +typedef ACE_Hash_Map_Reverse_Iterator_Ex<KEY, CACHE_VALUE, Hash_Key, ACE_Equal_To<KEY>, ACE_Null_Mutex> + HASH_MAP_REVERSE_ITERATOR; + +typedef ACE_Map_Manager<KEY, CACHE_VALUE, ACE_Null_Mutex> + MAP_MANAGER; +typedef ACE_Map_Iterator<KEY, CACHE_VALUE, ACE_Null_Mutex> + MAP_ITERATOR; +typedef ACE_Map_Reverse_Iterator<KEY, CACHE_VALUE, ACE_Null_Mutex> + MAP_REVERSE_ITERATOR; + +typedef ACE_Cleanup_Strategy<KEY, CACHE_VALUE, HASH_MAP_MANAGER> + HASH_MAP_CLEANUP; + +typedef ACE_Cleanup_Strategy<KEY, CACHE_VALUE, MAP_MANAGER> + MAP_CLEANUP; + +typedef ACE_Pair_Caching_Utility<KEY, CACHE_VALUE, HASH_MAP_MANAGER, HASH_MAP_ITERATOR, ATTR> + HASH_MAP_CACHING_UTILITY; + +typedef ACE_Pair_Caching_Utility<KEY, CACHE_VALUE, MAP_MANAGER, MAP_ITERATOR, ATTR> + MAP_CACHING_UTILITY; + +// = Hash_Map_Manager related +typedef ACE_Caching_Strategy<ATTR, HASH_MAP_CACHING_UTILITY> + HASH_MAP_CACHING_STRATEGY; +typedef ACE_LRU_Caching_Strategy<ATTR, HASH_MAP_CACHING_UTILITY> + HASH_MAP_LRU; +typedef ACE_LFU_Caching_Strategy<ATTR, HASH_MAP_CACHING_UTILITY> + HASH_MAP_LFU; +typedef ACE_FIFO_Caching_Strategy<ATTR, HASH_MAP_CACHING_UTILITY> + HASH_MAP_FIFO; +typedef ACE_Null_Caching_Strategy<ATTR, HASH_MAP_CACHING_UTILITY> + HASH_MAP_NULL; +typedef ACE_Caching_Strategy_Adapter<ATTR, HASH_MAP_CACHING_UTILITY, HASH_MAP_LRU> + HASH_MAP_LRU_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTR, HASH_MAP_CACHING_UTILITY, HASH_MAP_LFU> + HASH_MAP_LFU_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTR, HASH_MAP_CACHING_UTILITY, HASH_MAP_FIFO> + HASH_MAP_FIFO_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTR, HASH_MAP_CACHING_UTILITY, HASH_MAP_NULL> + HASH_MAP_NULL_ADAPTER; + +// = Map_Manager related +typedef ACE_Caching_Strategy<ATTR, MAP_CACHING_UTILITY> + MAP_CACHING_STRATEGY; +typedef ACE_LRU_Caching_Strategy<ATTR, MAP_CACHING_UTILITY> + MAP_LRU; +typedef ACE_LFU_Caching_Strategy<ATTR, MAP_CACHING_UTILITY> + MAP_LFU; +typedef ACE_FIFO_Caching_Strategy<ATTR, MAP_CACHING_UTILITY> + MAP_FIFO; +typedef ACE_Null_Caching_Strategy<ATTR, MAP_CACHING_UTILITY> + MAP_NULL; +typedef ACE_Caching_Strategy_Adapter<ATTR, MAP_CACHING_UTILITY, MAP_LRU> + MAP_LRU_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTR, MAP_CACHING_UTILITY, MAP_LFU> + MAP_LFU_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTR, MAP_CACHING_UTILITY, MAP_FIFO> + MAP_FIFO_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTR, MAP_CACHING_UTILITY, MAP_NULL> + MAP_NULL_ADAPTER; + +typedef ACE_Hash_Cache_Map_Manager<KEY, VALUE, Hash_Key, ACE_Equal_To<KEY>, HASH_MAP_CACHING_STRATEGY, ATTR> + HASH_MAP_CACHE; +typedef ACE_Cache_Map_Manager<KEY, VALUE, MAP_MANAGER, MAP_ITERATOR, MAP_REVERSE_ITERATOR, MAP_CACHING_STRATEGY, ATTR> + MAP_CACHE; + +enum Caching_Strategy_Type +{ + ACE_LFU, + ACE_FIFO, + ACE_LRU, + ACE_NULL, + ACE_ALL +}; + +static size_t iterations = ACE_MAX_ITERATIONS; +static size_t no_of_lookups = iterations / 2; +static int randomize_lookups = 1; +static int purge_percent = 10; +static int debug = 0; +static Caching_Strategy_Type caching_strategy_type = ACE_ALL; +static KEY *lookup_array = 0; + +static void +run_iterator_cache (MAP_CACHE &cache) +{ + size_t iterations = cache.current_size (); + size_t counter = 0; + MAP_CACHE::iterator end = cache.end (); + + for (MAP_CACHE::iterator iter = cache.begin (); + iter != end; + ++iter) + { + // Debugging info. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d)"), + (*iter).first (), + (*iter).second ())); + + ACE_ASSERT ((*iter).first () == (*iter).second ()); + ++counter; + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + + ACE_UNUSED_ARG (iterations); + ACE_ASSERT (counter == iterations); +} + +static void +run_iterator_hash_cache (HASH_MAP_CACHE &cache) +{ + size_t iterations = cache.current_size (); + size_t counter = 0; + HASH_MAP_CACHE::iterator end = cache.end (); + + for (HASH_MAP_CACHE::iterator iter = cache.begin (); + iter != end; + ++iter) + { + // Debugging info. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d)"), + (*iter).first (), + (*iter).second ())); + + ACE_ASSERT ((*iter).first () == (*iter).second ()); + ++counter; + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + + ACE_UNUSED_ARG (iterations); + ACE_ASSERT (counter == iterations); +} + +static void +run_reverse_iterator_cache (MAP_CACHE &cache) +{ + size_t counter = cache.current_size (); + MAP_CACHE::reverse_iterator rend = cache.rend (); + + for (MAP_CACHE::reverse_iterator iter = cache.rbegin (); + iter != rend; + ++iter) + { + ACE_ASSERT ((*iter).first () == (*iter).second ()); + + // Debugging info. + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d)"), + (*iter).first (), + (*iter).second ())); + --counter; + } + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + + ACE_ASSERT (counter == 0); +} + +static void +run_reverse_iterator_hash_cache (HASH_MAP_CACHE &cache) +{ + size_t counter = cache.current_size (); + HASH_MAP_CACHE::reverse_iterator rend = cache.rend (); + + for (HASH_MAP_CACHE::reverse_iterator iter = cache.rbegin (); + iter != rend; + ++iter) + { + ACE_ASSERT ((*iter).first () == (*iter).second ()); + + // Debugging info. + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d)"), + (*iter).first (), + (*iter).second ())); + --counter; + } + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + + ACE_ASSERT (counter == 0); +} + +static void +find_test_cache (MAP_CACHE &cache) +{ + for (size_t i = 0; i < no_of_lookups; ++i) + { + VALUE j = 0; + int result = cache.find (lookup_array[i], j); + + ACE_ASSERT (result != -1); + ACE_ASSERT (j == lookup_array[i]); + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%d "), j)); + + ACE_UNUSED_ARG (result); + } + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); +} + +static void +find_test_hash_cache (HASH_MAP_CACHE &cache) +{ + for (size_t i = 0; i < no_of_lookups; ++i) + { + VALUE j = 0; + int result = cache.find (lookup_array[i], j); + + ACE_ASSERT (result != -1); + ACE_ASSERT (j == lookup_array[i]); + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%d "), j)); + + ACE_UNUSED_ARG (result); + } + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); +} + +static void +purge_test_cache (MAP_CACHE &cache) +{ + // Get the number of entries in the container. + size_t current_map_size = cache.current_size (); + + // Find the number of entries which will get purged. + size_t entries_to_remove = size_t ((double (purge_percent) / 100 * current_map_size) + 0.5); + + // Tell the caching strategy how much to purge. + cache.caching_strategy ().purge_percent (purge_percent); + + // Purge from cache. + int result = cache.purge (); + ACE_ASSERT (result != -1); + ACE_UNUSED_ARG (result); + + size_t resultant_size = 0; + if (caching_strategy_type == ACE_NULL) + resultant_size = current_map_size; + else + resultant_size = current_map_size - entries_to_remove; + + // Make sure the purge took out the appropriate number of entries. + ACE_ASSERT (cache.current_size () == resultant_size); + ACE_UNUSED_ARG (resultant_size); +} + +static void +purge_test_hash_cache (HASH_MAP_CACHE &cache) +{ + // Get the number of entries in the container. + size_t current_map_size = cache.current_size (); + + // Find the number of entries which will get purged. + size_t entries_to_remove = size_t ((double (purge_percent) / 100 * current_map_size) + 0.5); + + // Tell the caching strategy how much to purge. + cache.caching_strategy ().purge_percent (purge_percent); + + // Purge from cache. + int result = cache.purge (); + ACE_ASSERT (result != -1); + ACE_UNUSED_ARG (result); + + size_t resultant_size = 0; + if (caching_strategy_type == ACE_NULL) + resultant_size = current_map_size; + else + resultant_size = current_map_size - entries_to_remove; + + // Make sure the purge took out the appropriate number of entries. + ACE_ASSERT (cache.current_size () == resultant_size); + ACE_UNUSED_ARG (resultant_size); +} + +static void +functionality_test_cache (MAP_CACHING_STRATEGY &caching_strategy) +{ + MAP_CACHE cache (caching_strategy); + KEY i = 0; + VALUE j = 0; + + // Add it to the map now. + for (size_t counter = 0; + i < iterations; + ++i, ++j) + { + int result = cache.bind (i, j); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("keys[%d]=%d value=[%d]=%d\n"), + i, i, j, j)); + ++counter; + ACE_ASSERT (cache.current_size () == counter); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Number of entries in cache before purging: %d\n"), + cache.current_size ())); + + run_iterator_cache (cache); + run_reverse_iterator_cache (cache); + + find_test_cache (cache); + + purge_test_cache (cache); + + run_iterator_cache (cache); + run_reverse_iterator_cache (cache); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Number of entries in cache after purging: %d\n"), + cache.current_size ())); +} + +static void +functionality_test_hash_cache (HASH_MAP_CACHING_STRATEGY &caching_strategy) +{ + HASH_MAP_CACHE cache (caching_strategy); + KEY i = 0; + VALUE j = 0; + + // Add it to the map now. + for (size_t counter = 0; + i < iterations; + ++i, ++j) + { + int result = cache.bind (i, j); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("keys[%d]=%d value=[%d]=%d\n"), + i, i, j, j)); + ++counter; + ACE_ASSERT (cache.current_size () == counter); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Number of entries in cache before purging: %d\n"), + cache.current_size ())); + + run_iterator_hash_cache (cache); + run_reverse_iterator_hash_cache (cache); + + find_test_hash_cache (cache); + + purge_test_hash_cache (cache); + + run_iterator_hash_cache (cache); + run_reverse_iterator_hash_cache (cache); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Number of entries in cache after purging: %d\n"), + cache.current_size ())); +} + +void +test_caching_strategy_type (void) +{ + HASH_MAP_CACHING_STRATEGY *hash_map_caching_strategy = 0; + MAP_CACHING_STRATEGY *map_caching_strategy = 0; + + switch (caching_strategy_type) + { + case ACE_NULL: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nNull_Caching_Strategy\n\n"))); + ACE_NEW (map_caching_strategy, + MAP_NULL_ADAPTER); + ACE_NEW (hash_map_caching_strategy, + HASH_MAP_NULL_ADAPTER); + break; + + case ACE_LRU: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nLRU_Caching_Strategy\n\n"))); + ACE_NEW (map_caching_strategy, + MAP_LRU_ADAPTER); + ACE_NEW (hash_map_caching_strategy, + HASH_MAP_LRU_ADAPTER); + break; + + case ACE_LFU: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nLFU_Caching_Strategy\n\n"))); + ACE_NEW (map_caching_strategy, + MAP_LFU_ADAPTER); + ACE_NEW (hash_map_caching_strategy, + HASH_MAP_LFU_ADAPTER); + break; + + case ACE_FIFO: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nFIFO_Caching_Strategy\n\n"))); + ACE_NEW (map_caching_strategy, + MAP_FIFO_ADAPTER); + ACE_NEW (hash_map_caching_strategy, + HASH_MAP_FIFO_ADAPTER); + break; + + case ACE_ALL: // Just to remove warnings! + break; + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("map cache\n"))); + functionality_test_cache (*map_caching_strategy); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nhash map cache\n"))); + functionality_test_hash_cache (*hash_map_caching_strategy); + + delete map_caching_strategy; + delete hash_map_caching_strategy; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("c:i:r:f:p:d")); + + int cc; + while ((cc = get_opt ()) != -1) + { + switch (cc) + { + case 'c': + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("null")) == 0) + caching_strategy_type = ACE_NULL; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("lru")) == 0) + caching_strategy_type = ACE_LRU; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("lfu")) == 0) + caching_strategy_type = ACE_LFU; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("fifo")) == 0) + caching_strategy_type = ACE_FIFO; + break; + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'f': + no_of_lookups = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'r': + randomize_lookups = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'p': + purge_percent = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = 1; + break; + case '?': + case 'h': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s ") + ACE_TEXT ("[-c (caching strategy: lru / lfu / fifo / null [default = all])] ") + ACE_TEXT ("[-r (randomize lookups)] ") + ACE_TEXT ("[-i (iterations)] ") + ACE_TEXT ("[-d (debug, i.e., addition printouts)] ") + ACE_TEXT ("[-p (purge percent)] ") + ACE_TEXT ("[-f (number of lookups)] \n"), + ACE_TEXT ("Cache_Map_Manager_Test"))); + return -1; + } + } + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + // Validate options. + int result = parse_args (argc, argv); + if (result != 0) + return result; + + // Start the test only if options are valid. + ACE_START_TEST (ACE_TEXT ("Cache_Map_Manager_Test")); + + // Remove the extra debugging attributes from Log_Msg output. + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + // Providing random a unique seed. + ACE_OS::srand (static_cast<u_int> (ACE_OS::time (0))); + + // Create the lookup array. + ACE_NEW_RETURN (lookup_array, + KEY[no_of_lookups], + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nLookup sequence: "))); + + // Initialize the lookup array. + for (size_t k = 0; + k < no_of_lookups; + ++k) + { + if (randomize_lookups != 0) + lookup_array[k] = ACE_OS::rand () % iterations; + else + lookup_array[k] = k % iterations; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%d "), + lookup_array[k])); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\n"))); + + // Do we need to test all the strategies. + if (caching_strategy_type == ACE_ALL) + { + caching_strategy_type = ACE_NULL; + test_caching_strategy_type (); + + caching_strategy_type = ACE_LRU; + test_caching_strategy_type (); + + caching_strategy_type = ACE_LFU; + test_caching_strategy_type (); + + caching_strategy_type = ACE_FIFO; + test_caching_strategy_type (); + } + else + { + test_caching_strategy_type (); + } + + delete[] lookup_array; + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/Cache_Map_Manager_Test.h b/ACE/tests/Cache_Map_Manager_Test.h new file mode 100644 index 00000000000..6ca91ddf4de --- /dev/null +++ b/ACE/tests/Cache_Map_Manager_Test.h @@ -0,0 +1,38 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cache_Map_Manager_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#ifndef __ACE_CACHE_MAP_MANAGER_TEST_H +#define __ACE_CACHE_MAP_MANAGER_TEST_H + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +class Hash_Key +{ +public: + u_long operator () (size_t t) const + { + // Simply returns t + return static_cast<u_long> (t); + } +}; + +#endif /* __ACE_CACHE_MAP_MANAGER_TEST_T */ diff --git a/ACE/tests/Cached_Accept_Conn_Test.cpp b/ACE/tests/Cached_Accept_Conn_Test.cpp new file mode 100644 index 00000000000..700b2d7f5d7 --- /dev/null +++ b/ACE/tests/Cached_Accept_Conn_Test.cpp @@ -0,0 +1,525 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cached_Accept_Conn_Test.cpp +// +// = DESCRIPTION +// The test illustrates how the <ACE_Strategy_Connector> works by +// showing how you can cache connections on the client using +// different caching strategies. Also how connections can be purged +// explicitly and implicitly if needed from the connection cache +// maintained by the connector. The <ACE_Strategy_Acceptor> can also +// explicitly purge connections from the process CONNECTION CACHE on +// demand. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#ifndef CACHED_ACCEPT_CONNECTION_TEST +#define CACHED_ACCEPT_CONNECTION_TEST + +#include "test_config.h" + +// IBM C Set++ just can't grok the templates in here for auto template +// instantiation. It ends up overwriting a tempinc/*.C file and mashes +// its contents. +#if !defined (__xlC__) || (__xlC__ > 0x0301) + +#include "Cached_Accept_Conn_Test.h" + +#include "ace/OS_NS_string.h" +#include "ace/Get_Opt.h" + +#if defined(_MSC_VER) +#pragma warning(disable:4503) +#endif /* _MSC_VER */ + +ACE_RCSID(tests, Cached_Accept_Conn_Test, "$Id$") + +// Note: To keep both sunCC5.0 without debugging symbols and gcc2.7.3 +// happy, it was necessary to have the definitions of the methods of +// the Accept_Strategy before the instantiations. + +// HPUX doesn't accept these declaration after their usage. + +// For some strange reason this must *not* be static since otherwise +// certain versions of SunC++ will not link properly. +int connection_accepted = 0; + +// For some strange reason this must *not* be static since otherwise +// certain versions of SunC++ will not link properly. +int debug = 0; + +template <class SVC_HANDLER, ACE_PEER_ACCEPTOR_1> +Accept_Strategy<SVC_HANDLER, ACE_PEER_ACCEPTOR_2>::Accept_Strategy (CACHED_CONNECT_STRATEGY &caching_connect_strategy) + : caching_connect_strategy_ (caching_connect_strategy) +{ +} + +template <class SVC_HANDLER, ACE_PEER_ACCEPTOR_1> int +Accept_Strategy<SVC_HANDLER, ACE_PEER_ACCEPTOR_2>::open (const ACE_PEER_ACCEPTOR_ADDR &local_addr, + int restart) +{ + int result = ACCEPT_STRATEGY_BASE::open (local_addr, + restart); + + if (result == 0) + return result; + + // If the error occured due to the fact that the file descriptor + // limit was exhausted, then purge the connection cache of some + // entries. + result = this->out_of_sockets_handler (); + if (result == -1) + return -1; + + // If we are able to purge, try again. + return ACCEPT_STRATEGY_BASE::open (local_addr, restart); +} + +template <class SVC_HANDLER, ACE_PEER_ACCEPTOR_1> int +Accept_Strategy<SVC_HANDLER, ACE_PEER_ACCEPTOR_2>::accept_svc_handler (SVC_HANDLER *svc_handler) +{ + // Stop the event loop. + connection_accepted = 1; + + // Try to find out if the implementation of the reactor that we are + // using requires us to reset the event association for the newly + // created handle. This is because the newly created handle will + // inherit the properties of the listen handle, including its event + // associations. + int reset_new_handle = this->reactor_->uses_event_associations (); + + int result = this->acceptor ().accept (svc_handler->peer (), // stream + 0, // remote address + 0, // timeout + 1, // restart + reset_new_handle // reset new handler + ); + if (result == 0) + { + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Accept succeeded with handle %d\n"), + svc_handler->get_handle ())); + return result; + } + + // If the error occured due to the fact that the file descriptor + // limit was exhausted, then purge the connection cache of some + // entries. + if (0 != this->out_of_sockets_handler ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("out_of_sockets_handler"))); + + // Close down handler to avoid memory leaks. + svc_handler->close (0); + return -1; +} + +template <class SVC_HANDLER, ACE_PEER_ACCEPTOR_1> int +Accept_Strategy<SVC_HANDLER, ACE_PEER_ACCEPTOR_2>::out_of_sockets_handler (void) +{ + if (ACE::out_of_handles (errno)) + { + // Close connections which are cached by explicitly purging the + // connection cache maintained by the connector. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Purging connections from Connection Cache...\n"))); + + return this->caching_connect_strategy_.purge_connections (); + } + + return -1; +} + +typedef Accept_Strategy<Server_Svc_Handler, ACE_SOCK_ACCEPTOR> + ACCEPT_STRATEGY; + +Client_Svc_Handler::Client_Svc_Handler (ACE_Thread_Manager *t) + : ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> (t) +{ +} + +int +Client_Svc_Handler::open (void *) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("opening Client_Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + return 0; +} + +int +Client_Svc_Handler::close (u_long flags) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Closing Client_Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + return ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>::close (flags); +} + + +Server_Svc_Handler::Server_Svc_Handler (ACE_Thread_Manager *t) + : ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> (t) +{ +} + +int +Server_Svc_Handler::open (void *) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("opening Server_Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + + return this->close (); +} + +enum Caching_Strategy_Type +{ + ACE_LFU, + ACE_FIFO, + ACE_LRU, + ACE_NULL, + ACE_ALL +}; + +// Default number of clients/servers. +static int listen_once = 1; +static int user_has_specified_iterations = 0; +static size_t keep_handles_available = 100; +static double purge_percentage = 20; +static Caching_Strategy_Type caching_strategy_type = ACE_ALL; + +// On Win32, the handle gobbling doesn't work. Therefore, we need +// more iterations to get to the handle limit. +#if defined (ACE_WIN32) +static int iterations = 2000; +#else +static int iterations = 200; +#endif /* ACE_WIN32 */ + + +static int +cached_connect (STRATEGY_CONNECTOR &con, + const ACE_INET_Addr &server_addr) +{ + // This will make sure we get the host information correct. + ACE_INET_Addr remote_addr (server_addr.get_port_number (), + ACE_LOCALHOST); + + // Perform a blocking connect to the server using the Strategy + // Connector with a connection caching strategy. + Client_Svc_Handler *svc_handler = 0; + int result = con.connect (svc_handler, + remote_addr); + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("connection failed")), + -1); + else + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Connection successful to server at port %d!\n"), + remote_addr.get_port_number ())); + + // Reset Svc_Handler state. + svc_handler->recycle_state (ACE_RECYCLABLE_PURGABLE_BUT_NOT_IDLE); + + return 0; +} + +static void +server (void) +{ + int result = 1; + + while (connection_accepted == 0) + result = ACE_Reactor::instance ()->handle_events (); + + connection_accepted = 0; + + ACE_UNUSED_ARG (result); +} + +static void +test_connection_management (CACHING_STRATEGY &caching_strategy) +{ + // Configure the Strategy Connector with a strategy that caches + // connection. + CACHED_CONNECT_STRATEGY caching_connect_strategy (caching_strategy); + + NULL_CREATION_STRATEGY creation_strategy; + NULL_ACTIVATION_STRATEGY activation_strategy; + + STRATEGY_CONNECTOR strategy_connector (0, + &creation_strategy, + &caching_connect_strategy, + &activation_strategy); + + // Connect strategy is required by the <out_of_sockets_handler>. + ACCEPT_STRATEGY listen_one_time_accept_strategy (caching_connect_strategy); + + // If <listen_once> is true, only one Acceptor is used for the test. + ACCEPTOR listen_one_time_acceptor; + ACE_INET_Addr server_addr; + + int result = + listen_one_time_acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &), + ACE_Reactor::instance (), + 0, + &listen_one_time_accept_strategy); + if (result != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("acceptor open"))); + return; + } + + result = listen_one_time_acceptor.acceptor ().get_local_addr (server_addr); + if (result != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("acceptor getaddr"))); + listen_one_time_acceptor.close (); + return; + } + + for (int i = 1; i <= iterations; ++i) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("iteration %d\n"), + i)); + + // Connect strategy is required by the <out_of_sockets_handler>. + ACCEPT_STRATEGY listen_multiple_times_accept_strategy (caching_connect_strategy); + + // If <listen_once> is false, one Acceptor is used for every + // iteration. + ACCEPTOR listen_multiple_times_acceptor; + + if (!listen_once) + { + // Bind acceptor to any port and then find out what the port + // was. + if (listen_multiple_times_acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &), + ACE_Reactor::instance (), + 0, + &listen_multiple_times_accept_strategy) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open"))); + break; + } + + if (listen_multiple_times_acceptor.acceptor ().get_local_addr (server_addr) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr"))); + break; + } + + } + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("starting server at port %d\n"), + server_addr.get_port_number ())); + + // Run the cached blocking test. + if (-1 == cached_connect (strategy_connector, server_addr)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("cached_connect"))); + else + server (); + } +} + +void +test_caching_strategy_type (void) +{ + CACHING_STRATEGY *caching_strategy = 0; + + switch (caching_strategy_type) + { + case ACE_NULL: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nNull_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + NULL_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_LRU: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nLRU_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + LRU_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_LFU: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nLFU_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + LFU_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_FIFO: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nFIFO_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + FIFO_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_ALL: // Just to remove warnings! + break; + } + + caching_strategy->purge_percent (purge_percentage); + test_connection_management (*caching_strategy); + delete caching_strategy; +} + +int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("l:i:p:c:a:d")); + + int cc; + + while ((cc = get_opt ()) != -1) + switch (cc) + { + case 'd': + debug = 1; + break; + case 'l': + listen_once = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + user_has_specified_iterations = 1; + break; + case 'p': + purge_percentage = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + // Note that if null caching strategy is used then this test + // will fail if the number of servers exceed number of open + // files allowed for the process. + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("null")) == 0) + caching_strategy_type = ACE_NULL; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("lru")) == 0) + caching_strategy_type = ACE_LRU; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("lfu")) == 0) + caching_strategy_type = ACE_LFU; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("fifo")) == 0) + caching_strategy_type = ACE_FIFO; + break; + case 'a': + keep_handles_available = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case '?': + case 'h': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s ") + ACE_TEXT ("[-t (timeout)] ") + ACE_TEXT ("[-c (caching strategy: lru / lfu / fifo / null [default = all])] ") + ACE_TEXT ("[-i (iterations)] ") + ACE_TEXT ("[-l (listen once)] ") + ACE_TEXT ("[-d (addition debugging output)] ") + ACE_TEXT ("[-p (purge percent)] ") + ACE_TEXT ("[-a (keep handles available)] "), + ACE_TEXT ("Cached_Accept_Conn_Test"))); + return -1; + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + // Validate options. + int result = parse_args (argc, argv); + if (result != 0) + return result; + +#if defined (ACE_WIN32) + // Somehow, on Win32, the <listen once> option allows us to create + // more handles. + if (!user_has_specified_iterations && + listen_once) + iterations *= 2; +#endif /* ACE_WIN32 */ + + // Start the test only if options are valid. + ACE_START_TEST (ACE_TEXT ("Cached_Accept_Conn_Test")); + + // Remove the extra debugging attributes from Log_Msg output. + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + // The reactor's constructor changes the handle limit for the + // process. + ACE_Reactor::instance (); + + // Consume all handles in the process, leaving us + // <keep_handles_available> to play with. + ACE_Handle_Gobbler handle_gobbler; + if (0 != handle_gobbler.consume_handles (keep_handles_available)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("handle_gobbler"))); + + // Do we need to test all the strategies. Note, that the less + // useful null strategy is ignored in this case. + if (caching_strategy_type == ACE_ALL) + { + caching_strategy_type = ACE_LRU; + test_caching_strategy_type (); + + // Default iterations are too many; if the user hasn't specified + // otherwise, we'll shrink the iterations for LFU and FIFO. + if (!user_has_specified_iterations) + iterations /= 100; + + caching_strategy_type = ACE_LFU; + test_caching_strategy_type (); + + caching_strategy_type = ACE_FIFO; + test_caching_strategy_type (); + } + else + { + test_caching_strategy_type (); + } + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + +#else /* Do this for C Set++ 3.1 */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Cached_Accept_Conn_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("C Set++ won't build this test correctly\n"))); + +#endif /* !__xlC__ || __xlC > 0x0301 */ + + ACE_END_TEST; + return 0; +} + + +#endif /* CACHED_ACCEPT_CONNECTION_TEST */ diff --git a/ACE/tests/Cached_Accept_Conn_Test.h b/ACE/tests/Cached_Accept_Conn_Test.h new file mode 100644 index 00000000000..6b8b3e8f46a --- /dev/null +++ b/ACE/tests/Cached_Accept_Conn_Test.h @@ -0,0 +1,137 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cache_Accept_Conn_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_CACHED_ACCEPT_CONN_TEST_H +#define ACE_TESTS_CACHED_ACCEPT_CONN_TEST_H + +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/INET_Addr.h" +#include "ace/Acceptor.h" +#include "ace/Connector.h" +#include "ace/Svc_Handler.h" +#include "ace/Caching_Utility_T.h" +#include "ace/Cached_Connect_Strategy_T.h" +#include "ace/Handle_Gobbler.h" + +class Client_Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + + Client_Svc_Handler (ACE_Thread_Manager *t = 0); + int open (void *v = 0); + int close (u_long flags = 0); +}; + +class Server_Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + + Server_Svc_Handler (ACE_Thread_Manager *t = 0); + int open (void *v = 0); +}; + +typedef size_t ATTRIBUTES; +typedef ACE_Pair<Client_Svc_Handler *, ATTRIBUTES> + CACHED_HANDLER; +typedef ACE_Refcounted_Hash_Recyclable<ACE_INET_Addr> + ACE_ADDR; +typedef ACE_Hash<ACE_ADDR> H_KEY; +typedef ACE_Equal_To<ACE_ADDR> C_KEYS; + +typedef ACE_Hash_Map_Manager_Ex<ACE_ADDR, CACHED_HANDLER, H_KEY, C_KEYS, ACE_Null_Mutex> + HASH_MAP; +typedef ACE_Hash_Map_Iterator_Ex<ACE_ADDR, CACHED_HANDLER, H_KEY, C_KEYS, ACE_Null_Mutex> + HASH_MAP_ITERATOR; +typedef ACE_Hash_Map_Reverse_Iterator_Ex<ACE_ADDR, CACHED_HANDLER, H_KEY, C_KEYS, ACE_Null_Mutex> + HASH_MAP_REVERSE_ITERATOR; + +typedef ACE_Recyclable_Handler_Cleanup_Strategy<ACE_ADDR, CACHED_HANDLER, HASH_MAP> + CLEANUP_STRATEGY; +typedef ACE_Recyclable_Handler_Caching_Utility<ACE_ADDR, CACHED_HANDLER, HASH_MAP, HASH_MAP_ITERATOR, ATTRIBUTES> + CACHING_UTILITY; + +typedef ACE_LRU_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + LRU_CACHING_STRATEGY; + +typedef ACE_LFU_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + LFU_CACHING_STRATEGY; +typedef ACE_FIFO_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + FIFO_CACHING_STRATEGY; +typedef ACE_Null_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + NULL_CACHING_STRATEGY; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, LRU_CACHING_STRATEGY> + LRU_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, LFU_CACHING_STRATEGY> + LFU_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, FIFO_CACHING_STRATEGY> + FIFO_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, NULL_CACHING_STRATEGY> + NULL_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + CACHING_STRATEGY; + +typedef ACE_Strategy_Acceptor<Server_Svc_Handler, ACE_SOCK_ACCEPTOR> + ACCEPTOR; + +typedef ACE_Strategy_Connector<Client_Svc_Handler, ACE_SOCK_CONNECTOR> + STRATEGY_CONNECTOR; + +typedef ACE_NOOP_Creation_Strategy<Client_Svc_Handler> + NULL_CREATION_STRATEGY; + +typedef ACE_NOOP_Concurrency_Strategy<Client_Svc_Handler> + NULL_ACTIVATION_STRATEGY; + +typedef ACE_Cached_Connect_Strategy_Ex<Client_Svc_Handler, ACE_SOCK_CONNECTOR, CACHING_STRATEGY, ATTRIBUTES, ACE_SYNCH_NULL_MUTEX> + CACHED_CONNECT_STRATEGY; + + +template <class SVC_HANDLER, ACE_PEER_ACCEPTOR_1> +class Accept_Strategy : public ACE_Accept_Strategy<SVC_HANDLER, ACE_PEER_ACCEPTOR_2> +{ +public: + + Accept_Strategy (CACHED_CONNECT_STRATEGY &caching_connect_strategy); + // Constructor. + + int open (const ACE_PEER_ACCEPTOR_ADDR &local_addr, + int restart = 0); + // Initialize the <peer_acceptor_> with <local_addr>. If the + // process runs out of descriptors, the unsed svc_handlers from the + // CONNECTION CACHE are removed. + + int accept_svc_handler (SVC_HANDLER *svc_handler); + // The default behavior delegates to the <accept> method of the + // PEER_ACCEPTOR. A check is made here for the process running out + // of file descriptors. If so, the CONNECTION CACHE is purged of + // some idle svc_handlers. + +protected: + + typedef ACE_Accept_Strategy<SVC_HANDLER, ACE_PEER_ACCEPTOR_2> ACCEPT_STRATEGY_BASE; + + int out_of_sockets_handler (void); + // Handler for removing cached connections. + + CACHED_CONNECT_STRATEGY &caching_connect_strategy_; +}; + +#endif /* ACE_TESTS_CACHED_ACCEPT_CONN_TEST_H */ diff --git a/ACE/tests/Cached_Allocator_Test.cpp b/ACE/tests/Cached_Allocator_Test.cpp new file mode 100644 index 00000000000..1bd3eef6aaa --- /dev/null +++ b/ACE/tests/Cached_Allocator_Test.cpp @@ -0,0 +1,254 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cached_Allocator_Test.cpp +// +// = DESCRIPTION +// Simple test of ACE_Dynamic_Cached_Allocator and ACE_Cached_Allocator. +// +// = AUTHOR +// Jaroslaw Nozderko <jareknz@polbox.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Malloc_T.h" +#include "ace/High_Res_Timer.h" + +ACE_RCSID(tests, Cached_Allocator_Test, "$Id$") + +#include "ace/Synch_Traits.h" +#include "ace/Null_Mutex.h" + +typedef ACE_Dynamic_Cached_Allocator<ACE_SYNCH_NULL_MUTEX> DYNAMIC_ALLOCATOR; + +static int +speed_test (ACE_UINT32 loops) +{ + double tt = 0.0, + ut = 0.0, + utus = 0.0, + speed = 0.0; + + ACE_Time_Value tc; + void *ptr = 0; + ACE_UINT32 i = loops; + size_t n_chunks = 10; + size_t chunk_size = 8; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) ACE_Dynamic_Cached_Allocator ") + ACE_TEXT ("speed test...\n"))); + + DYNAMIC_ALLOCATOR allocator (n_chunks, chunk_size); + + ACE_High_Res_Timer timer; + timer.reset (); + + timer.start (); + + while (i--) + { + ptr = allocator.malloc (chunk_size); + allocator.free (ptr); + } + + timer.stop (); + + timer.elapsed_time (tc); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Iterations : %d\n"), loops)); + tt = tc.sec () + tc.usec ()*1.0e-6; + ut = tt/loops; + utus = ut*1.0e6; + speed = loops/tt; + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Total time : %.6g [s]\n"), tt)); + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Unit time : %.6g [us]\n"), utus)); + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Speed : %.6g [1/s]\n"), speed)); + + return 0; +} + +typedef char MEMBLOCK[8]; +typedef ACE_Cached_Allocator<MEMBLOCK, ACE_SYNCH_NULL_MUTEX> STATIC_ALLOCATOR; + +static int +stdspeed_test (ACE_UINT32 loops) +{ + + double tt = 0.0, + ut = 0.0, + utus = 0.0, + speed = 0.0; + + ACE_Time_Value tc; + void *ptr = 0; + ACE_UINT32 i = loops; + size_t n_chunks = 10, + chunk_size = 8; + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) ACE_Cached_Allocator ") + ACE_TEXT ("speed test...\n"))); + + STATIC_ALLOCATOR allocator (n_chunks); + + ACE_High_Res_Timer timer; + timer.reset (); + + timer.start (); + while (i--) + { + ptr = allocator.malloc (chunk_size); + allocator.free (ptr); + } + timer.stop (); + + timer.elapsed_time (tc); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Iterations : %d\n"), loops)); + tt = tc.sec () + tc.usec ()*1.0e-6; + ut = tt/loops; + utus = ut*1.0e6; + speed = loops/tt; + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Total time : %.6g [s]\n"), tt)); + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Unit time : %.6g [us]\n"), utus)); + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Speed : %.6g [1/s]\n"), speed)); + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Cached_Allocator_Test")); + + size_t chunk_size = 0; + size_t n_chunks = 0; + size_t requested_size = 0; + size_t depth = 0; + char *ptr1 = 0; + char *ptr2 = 0; + char *ptr3 = 0; + char *ptr4 = 0; + ACE_UINT32 loops = 0; + + const char *str1 = "12345678"; + const char *str3 = "ABCDEFGH"; + + if (argc < 2) + loops = 10000000; + else + loops = ACE_OS::atoi (argv[1]); + + chunk_size = 8; + n_chunks = 2; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) Creating allocator: ") + ACE_TEXT ("%B chunks, %B bytes each\n"), + n_chunks, + chunk_size)); + + DYNAMIC_ALLOCATOR allocator (n_chunks, chunk_size); + + if ((depth = allocator.pool_depth ()) != n_chunks) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected pool depth %B but reported %B\n"), + n_chunks, depth)); + requested_size = chunk_size; + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) Allocating chunk 1: %B bytes, should succeed...\n"), + requested_size)); + + ptr1 = (char *) allocator.malloc (requested_size); + if (!ptr1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT (" (%t) Failed, exiting.\n")), -1); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) OK, succeeded.\n"))); + if ((depth = allocator.pool_depth ()) != (n_chunks - 1)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected pool depth %B but reported %B\n"), + n_chunks - 1, depth)); + + requested_size = chunk_size + 1; + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) Allocating chunk 2: %B bytes, too big, should fail...\n"), + requested_size)); + + ptr2 = (char *) allocator.malloc (requested_size); + if (!ptr2) + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) OK, failed.\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT (" (%t) Something is wrong...\n")), -1); + + requested_size = chunk_size - 1; + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) Allocating chunk 3: %B bytes, ") + ACE_TEXT ("should succeed...\n"), + requested_size)); + ptr3 = (char *) allocator.malloc (requested_size); + if (!ptr3) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT (" (%t) Failed, exiting.\n")), -1); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) OK, succeeded.\n"))); + + // One chunk too far... + if ((depth = allocator.pool_depth ()) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected pool depth 0 but reported %B\n"), + depth)); + requested_size = chunk_size; + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) Allocating chunk 4: %B bytes, no free chunks,") + ACE_TEXT (" should fail...\n"), + requested_size)); + + ptr4 = (char *) allocator.malloc (requested_size); + if (!ptr4) + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) OK, failed.\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT (" (%t) Something is wrong\n")), -1); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Writing to chunk 1: %C\n"), str1)); + ACE_OS::memcpy (ptr1, str1, chunk_size); + ptr1[chunk_size - 1] = '\0'; + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Reading from chunk 1: %C\n"), ptr1)); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Writing to chunk 3: %C\n"), str3)); + ACE_OS::memcpy (ptr3, str3, chunk_size); + ptr3[chunk_size - 1] = '\0'; + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Reading from chunk 3: %C\n"), ptr3)); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Deallocating chunk 1\n"))); + allocator.free (ptr1); + + requested_size = chunk_size; + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Allocating chunk: %B bytes, ") + ACE_TEXT ("should succeed...\n"), + requested_size)); + ptr1 = (char *) allocator.malloc (requested_size); + if (!ptr1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT (" (%t) Failed, exiting.\n")), -1); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) OK, succeeded.\n"))); + + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Deallocating chunk 1\n"))); + allocator.free (ptr1); + ACE_DEBUG ((LM_INFO, ACE_TEXT (" (%t) Deallocating chunk 3\n"))); + allocator.free (ptr3); + + speed_test (loops); + stdspeed_test (loops); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Cached_Conn_Test.cpp b/ACE/tests/Cached_Conn_Test.cpp new file mode 100644 index 00000000000..ce4ba892ed9 --- /dev/null +++ b/ACE/tests/Cached_Conn_Test.cpp @@ -0,0 +1,496 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cached_Conn_Test.cpp +// +// = DESCRIPTION +// The test illustrates how the <ACE_Strategy_Connector> works by +// showing how you can cache connections on the client using +// different caching strategies. Also how connections can be purged +// explicitly and implicitly if needed from the connection cache +// maintained by the connector. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#ifndef CACHED_CONNECT_TEST +#define CACHED_CONNECT_TEST + +#include "test_config.h" + +// IBM C Set++ just can't grok the templates in here for auto template +// instantiation. It ends up overwriting a tempinc/*.C file and mashes +// its contents. +#if !defined (__xlC__) || (__xlC__ > 0x0301) + +#include "Cached_Conn_Test.h" + +#include "ace/OS_NS_string.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Svc_Handler.h" +#include "ace/SOCK_Stream.h" +#include "ace/Acceptor.h" +#include "ace/Connector.h" +#include "ace/Get_Opt.h" +#include "ace/Caching_Utility_T.h" +#include "ace/Cached_Connect_Strategy_T.h" +#include "ace/Handle_Gobbler.h" + +#if defined(_MSC_VER) +#pragma warning(disable:4503) +#endif /* _MSC_VER */ + +ACE_RCSID(tests, Cached_Conn_Test, "$Id$") + +typedef size_t ATTRIBUTES; +typedef ACE_Pair<Svc_Handler *, ATTRIBUTES> + CACHED_HANDLER; +typedef ACE_Refcounted_Hash_Recyclable<ACE_INET_Addr> + ACE_ADDR; +typedef ACE_Hash<ACE_ADDR> H_KEY; +typedef ACE_Equal_To<ACE_ADDR> C_KEYS; + +typedef ACE_Hash_Map_Manager_Ex<ACE_ADDR, CACHED_HANDLER, H_KEY, C_KEYS, ACE_Null_Mutex> + HASH_MAP; +typedef ACE_Hash_Map_Iterator_Ex<ACE_ADDR, CACHED_HANDLER, H_KEY, C_KEYS, ACE_Null_Mutex> + HASH_MAP_ITERATOR; +typedef ACE_Hash_Map_Reverse_Iterator_Ex<ACE_ADDR, CACHED_HANDLER, H_KEY, C_KEYS, ACE_Null_Mutex> + HASH_MAP_REVERSE_ITERATOR; + +typedef ACE_Recyclable_Handler_Cleanup_Strategy<ACE_ADDR, CACHED_HANDLER, HASH_MAP> + CLEANUP_STRATEGY; +typedef ACE_Recyclable_Handler_Caching_Utility<ACE_ADDR, CACHED_HANDLER, HASH_MAP, HASH_MAP_ITERATOR, ATTRIBUTES> + CACHING_UTILITY; + +typedef ACE_LRU_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + LRU_CACHING_STRATEGY; + +typedef ACE_LFU_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + LFU_CACHING_STRATEGY; +typedef ACE_FIFO_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + FIFO_CACHING_STRATEGY; +typedef ACE_Null_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + NULL_CACHING_STRATEGY; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, LRU_CACHING_STRATEGY> + LRU_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, LFU_CACHING_STRATEGY> + LFU_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, FIFO_CACHING_STRATEGY> + FIFO_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy_Adapter<ATTRIBUTES, CACHING_UTILITY, NULL_CACHING_STRATEGY> + NULL_CACHING_STRATEGY_ADAPTER; +typedef ACE_Caching_Strategy<ATTRIBUTES, CACHING_UTILITY> + CACHING_STRATEGY; + +typedef ACE_Oneshot_Acceptor<Svc_Handler, ACE_SOCK_ACCEPTOR> + ACCEPTOR; + +typedef ACE_Strategy_Connector<Svc_Handler, ACE_SOCK_CONNECTOR> + STRATEGY_CONNECTOR; + +typedef ACE_NOOP_Creation_Strategy<Svc_Handler> + NULL_CREATION_STRATEGY; + +typedef ACE_NOOP_Concurrency_Strategy<Svc_Handler> + NULL_ACTIVATION_STRATEGY; + +typedef ACE_Cached_Connect_Strategy_Ex<Svc_Handler, ACE_SOCK_CONNECTOR, CACHING_STRATEGY, ATTRIBUTES, ACE_SYNCH_NULL_MUTEX> + CACHED_CONNECT_STRATEGY; + +#endif /* CACHED_CONNECT_TEST */ + +static int debug = 0; + +Svc_Handler::Svc_Handler (ACE_Thread_Manager *t) + : ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> (t) +{ +} + +int +Svc_Handler::open (void *) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("opening Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + + return 0; +} + +int +Svc_Handler::close (u_long flags) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Closing Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + return ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>::close (flags); +} + +enum Caching_Strategy_Type +{ + ACE_LFU, + ACE_FIFO, + ACE_LRU, + ACE_NULL, + ACE_ALL +}; + +// Default number of clients/servers. +static int listen_once = 1; +static int user_has_specified_iterations = 0; +static size_t keep_handles_available = 100; +static double purge_percentage = 20; +static Caching_Strategy_Type caching_strategy_type = ACE_ALL; +static CACHED_CONNECT_STRATEGY *connect_strategy = 0; + +// On Win32, the handle gobbling doesn't work. Therefore, we need +// more iterations to get to the handle limit. +#if defined (ACE_WIN32) +static int iterations = 2000; +#elif defined (__Lynx__) +static int iterations = 134; +#else +static int iterations = 200; +#endif /* ACE_WIN32 */ + +//==================================================================== + +static void +out_of_sockets_handler (void) +{ + if (ACE::out_of_handles (errno)) + { + // Close connections which are cached by explicitly purging the + // connection cache maintained by the connector. + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("Purging connections from Connection Cache...\n"))); + + if (-1 == connect_strategy->purge_connections ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("purge_connections"))); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("in out_of_sockets_handler, ") + ACE_TEXT ("but out_of_handles said no"))); + } +} + +static int +cached_connect (STRATEGY_CONNECTOR &con, + const ACE_INET_Addr &server_addr) +{ + // This will make sure we get the host information correct. + ACE_INET_Addr remote_addr (server_addr.get_port_number (), + ACE_LOCALHOST); + + // Perform a blocking connect to the server using the Strategy + // Connector with a connection caching strategy. + Svc_Handler *svc_handler = 0; + int result = con.connect (svc_handler, + remote_addr); + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("connection failed")), + -1); + + // Reset Svc_Handler state. + svc_handler->recycle_state (ACE_RECYCLABLE_PURGABLE_BUT_NOT_IDLE); + + return 0; +} + +static int +server (ACCEPTOR *acceptor) +{ + ACE_INET_Addr cli_addr; + + // Create a new <Svc_Handler> to consume the data. + Svc_Handler svc_handler; + + int result = acceptor->accept (&svc_handler, + &cli_addr); + if (result == -1) + return -1; + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("client %s connected from %d\n"), + cli_addr.get_host_name (), + cli_addr.get_port_number ())); + + // + // Svc_Handler dies here, closing the server side socket. + // + return 0; +} + +static void +test_connection_management (CACHING_STRATEGY &caching_strategy) +{ + // Configure the Strategy Connector with a strategy that caches + // connection. + CACHED_CONNECT_STRATEGY caching_connect_strategy (caching_strategy); + + // This is required by the <out_of_sockets_handler>. + connect_strategy = &caching_connect_strategy; + + NULL_CREATION_STRATEGY creation_strategy; + NULL_ACTIVATION_STRATEGY activation_strategy; + + STRATEGY_CONNECTOR strategy_connector (0, + &creation_strategy, + &caching_connect_strategy, + &activation_strategy); + + // If <listen_once> is true, only one Acceptor is used for the test. + ACCEPTOR listen_one_time_acceptor; + ACE_INET_Addr server_addr; + + if (0 != listen_one_time_acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &))) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("acceptor open"))); + return; + } + + if (0 != listen_one_time_acceptor.acceptor ().get_local_addr (server_addr)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("acceptor getaddr"))); + listen_one_time_acceptor.close (); + return; + } + + for (int i = 1; i <= iterations; ++i) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("iteration %d\n"), + i)); + + // If <listen_once> is false, one Acceptor is used for every + // iteration. + ACCEPTOR listen_multiple_times_acceptor; + + ACCEPTOR &acceptor = listen_once ? + listen_one_time_acceptor : + listen_multiple_times_acceptor; + + if (!listen_once) + { + // Bind acceptor to any port and then find out what the port + // was. + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1) + { + out_of_sockets_handler (); + continue; + } + + if (acceptor.acceptor ().get_local_addr (server_addr) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr"))); + break; + } + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("starting server at port %d\n"), + server_addr.get_port_number ())); + } + + // Run the cached blocking test. + if (-1 == cached_connect (strategy_connector, server_addr)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("cached_connect"))); + + if (-1 == server (&acceptor)) + out_of_sockets_handler (); + } +} + +void +test_caching_strategy_type (void) +{ + CACHING_STRATEGY *caching_strategy = 0; + + switch (caching_strategy_type) + { + case ACE_NULL: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nNull_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + NULL_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_LRU: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nLRU_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + LRU_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_LFU: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nLFU_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + LFU_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_FIFO: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nFIFO_Caching_Strategy\n\n"))); + ACE_NEW (caching_strategy, + FIFO_CACHING_STRATEGY_ADAPTER); + break; + + case ACE_ALL: // Just to remove warnings! + break; + } + + caching_strategy->purge_percent (purge_percentage); + test_connection_management (*caching_strategy); + delete caching_strategy; +} + +int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("l:i:p:c:a:d")); + + int cc; + + while ((cc = get_opt ()) != -1) + switch (cc) + { + case 'd': + debug = 1; + break; + case 'l': + listen_once = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + user_has_specified_iterations = 1; + break; + case 'p': + purge_percentage = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + // Note that if null caching strategy is used then this test + // will fail if the number of servers exceed number of open + // files allowed for the process. + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("null")) == 0) + caching_strategy_type = ACE_NULL; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("lru")) == 0) + caching_strategy_type = ACE_LRU; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("lfu")) == 0) + caching_strategy_type = ACE_LFU; + if (ACE_OS::strcmp (get_opt.opt_arg (), ACE_TEXT ("fifo")) == 0) + caching_strategy_type = ACE_FIFO; + break; + case 'a': + keep_handles_available = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case '?': + case 'h': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s ") + ACE_TEXT ("[-c (caching strategy: lru / lfu / fifo / null [default = all])] ") + ACE_TEXT ("[-i (iterations)] ") + ACE_TEXT ("[-l (listen once)] ") + ACE_TEXT ("[-d (addition debugging output)] ") + ACE_TEXT ("[-p (purge percent)] ") + ACE_TEXT ("[-a (keep handles available)] "), + ACE_TEXT ("Cached_Conn_Test"))); + return -1; + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + // Validate options. + int result = parse_args (argc, argv); + if (result != 0) + return result; + +#if defined (ACE_WIN32) + // Somehow, on Win32, the <listen once> option allows us to create + // more handles. + if (!user_has_specified_iterations && + listen_once) + iterations *= 2; +#endif /* ACE_WIN32 */ + + // Start the test only if options are valid. + ACE_START_TEST (ACE_TEXT ("Cached_Conn_Test")); + + // Remove the extra debugging attributes from Log_Msg output. + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + // The reactor's constructor changes the handle limit for the + // process. + ACE_Reactor::instance (); + + // Consume all handles in the process, leaving us + // <keep_handles_available> to play with. + ACE_Handle_Gobbler handle_gobbler; + if (0 != handle_gobbler.consume_handles (keep_handles_available)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("handle_gobbler"))); + + // Do we need to test all the strategies. Note, that the less + // useful null strategy is ignored in this case. + if (caching_strategy_type == ACE_ALL) + { + caching_strategy_type = ACE_LRU; + test_caching_strategy_type (); + + // Default iterations are too many; if the user hasn't specified + // otherwise, we'll shrink the iterations for LFU and FIFO. + if (!user_has_specified_iterations) + iterations /= 100; + + caching_strategy_type = ACE_LFU; + test_caching_strategy_type (); + + caching_strategy_type = ACE_FIFO; + test_caching_strategy_type (); + } + else + { + test_caching_strategy_type (); + } + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + +#else /* Do this for C Set++ 3.1 */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Cached_Conn_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("C Set++ won't build this test correctly\n"))); + +#endif /* !__xlC__ || __xlC > 0x0301 */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Cached_Conn_Test.h b/ACE/tests/Cached_Conn_Test.h new file mode 100644 index 00000000000..4e7cfa4673e --- /dev/null +++ b/ACE/tests/Cached_Conn_Test.h @@ -0,0 +1,35 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Cache_Conn_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_CACHED_CONN_TEST_H +#define ACE_TESTS_CACHED_CONN_TEST_H + +#include "ace/SOCK_Stream.h" +#include "ace/Svc_Handler.h" + +class Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + + Svc_Handler (ACE_Thread_Manager *t = 0); + int open (void *v = 0); + int close (u_long flags = 0); +}; + +#endif /* ACE_TESTS_CACHED_ACCEPT_CONN_TEST_H */ diff --git a/ACE/tests/Capabilities_Test.cpp b/ACE/tests/Capabilities_Test.cpp new file mode 100644 index 00000000000..bf3231b0ca3 --- /dev/null +++ b/ACE/tests/Capabilities_Test.cpp @@ -0,0 +1,119 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Capabilities_Test.cpp +// +// = DESCRIPTION +// This is a test that makes sure the <ACE_Capabililties> class +// works correctly. +// +// = AUTHOR +// Arturo Montes <mitosys@colomsat.net.co> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Capabilities.h" + +ACE_RCSID(tests, Capabilities_Test, "$Id$") + +static const ACE_TCHAR config[] = ACE_TEXT ("Capabilities_Test.cfg"); + +static int +load_config (void) +{ + ACE_Capabilities caps; + if (caps.getent (config, ACE_TEXT ("Config")) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Can't read %s\n"), + config), + 1); + + int b = 0; + caps.getval (ACE_TEXT ("bool"), b); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("bool = %d\n"), + b)); + + int n = 0; + caps.getval (ACE_TEXT ("integer"), n); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("integer = %d\n"), + n)); + + ACE_TString s; + caps.getval (ACE_TEXT ("string"), s); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("string = %s\n"), + s.c_str ())); + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Capabilities_Test")); + + + // -------------------------------------------------------- + // Create config file + // -------------------------------------------------------- + + // A config file is created within the test so that the test is + // completely self contained. + + const char file_contents[] = + "Config|Esta entrada reservada para la configuracion,\n" + " bool,\n" + " integer#2,\n" + " string=000030,\n\n"; + + ACE_HANDLE fd = ACE_OS::open (config, + O_RDWR | O_CREAT | O_TRUNC, + ACE_DEFAULT_FILE_PERMS); + + if (fd == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::open")), + -1); + + + if (ACE_OS::write (fd, file_contents, sizeof(file_contents)) != + sizeof(file_contents)) + { + ACE_OS::unlink (config); + ACE_OS::close (fd); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::write")), + -1); + } + + if (ACE_OS::close (fd) != 0) + { + ACE_OS::unlink (config); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::close")), + -1); + } + // -------------------------------------------------------- + + + int const result = load_config (); + + ACE_OS::unlink (config); + + ACE_END_TEST; + return result; +} diff --git a/ACE/tests/Codecs_Test.cpp b/ACE/tests/Codecs_Test.cpp new file mode 100644 index 00000000000..bad96176ce2 --- /dev/null +++ b/ACE/tests/Codecs_Test.cpp @@ -0,0 +1,119 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Codecs_Test.cpp +// +// = DESCRIPTION +// Checks the functionality of the ACE Codecs class. +// +// = AUTHORS +// Krishnakumar B <kitty@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Codecs.h" +#include "ace/Auto_Ptr.h" +#include "ace/ACE.h" + +ACE_RCSID(tests, Codecs_Test, "$Id$") + +// Don't change the strings thinking that they are typos + +const ACE_Byte normal_stream[] = "This is a sample test stream, to test simple Base64 encoding"; + +const ACE_Byte one_padded_stream[] = "This stream is different from the above in that, it results in one padding character to be adde"; + +const ACE_Byte two_padded_stream[] = "This stream is different from the above in that, it results in two padding characters to be addedddd"; + +int +encode_decode_stream (const ACE_Byte* stream, size_t length) +{ + size_t encode_len = 0; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Input stream = %C\n"), + stream)); + + ACE_Byte* encodeBuf = ACE_Base64::encode (stream, length, + &encode_len); + if (encodeBuf == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Error in encoding stream\n"))); + return -1; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Base64 encoded stream = %C\n"), + encodeBuf)); + + + ACE_Auto_Basic_Array_Ptr<ACE_Byte> cleanup_encodeBuf (encodeBuf); + + size_t decode_len = 0; + ACE_Byte* decodeBuf = ACE_Base64::decode (encodeBuf, &decode_len); + + if (decodeBuf == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error in decoding encoded stream\n"))); + return -1; + } + + ACE_Auto_Basic_Array_Ptr<ACE_Byte> cleanup_decodeBuf (decodeBuf); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Decoded Base64 encoded stream = %C\n"), + decodeBuf)); + + for (size_t i = 0; i < length; ++i) + if (decodeBuf[i] != stream[i]) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Encoded->Decoded stream differs from original stream\n"))); + return -1; + } + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Codecs_Test")); + int status = 0; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This is ACE Version %u.%u.%u\n\n"), + ACE::major_version (), + ACE::minor_version(), + ACE::beta_version())); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing ACE Base64 - normal stream\n\n"))); + + status = encode_decode_stream (normal_stream, sizeof (normal_stream) - 1); + + if (status == 0) { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing ACE Base64 - one padded stream\n\n"))); + status = encode_decode_stream (one_padded_stream, + sizeof (one_padded_stream) - 1); + } + if (status == 0) { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing ACE Base64 - two padded stream\n\n"))); + status = encode_decode_stream (two_padded_stream, + sizeof (two_padded_stream) - 1); + } + ACE_END_TEST; + return status; +} + diff --git a/ACE/tests/Collection_Test.cpp b/ACE/tests/Collection_Test.cpp new file mode 100644 index 00000000000..cf8e124de77 --- /dev/null +++ b/ACE/tests/Collection_Test.cpp @@ -0,0 +1,184 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Collection_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the ACE collection classes and its +// iterators. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID(tests, Collection_Test, "$Id$") + +#include "ace/Containers.h" +#include "Collection_Test.h" + +UglyThing::UglyThing (void* alloc, deletion_func dfunc) + : alloc_ (alloc) + , dfunc_ (dfunc) +{ +} + +bool +UglyThing::operator== (const UglyThing& r) const +{ + return this->alloc_ == r.alloc_; +} + +typedef UglyThing DATA; +typedef ACE_Unbounded_Set<DATA> UNBOUNDED_SET; +typedef ACE_Unbounded_Set_Iterator<DATA> UNBOUNDED_SET_ITERATOR; +typedef ACE_Unbounded_Set_Const_Iterator<DATA> UNBOUNDED_SET_CONST_ITERATOR; + +typedef int ARRAY_DATA; +typedef ACE_Array<ARRAY_DATA> ARRAY; +typedef ACE_Array_Iterator<ARRAY_DATA> ARRAY_ITERATOR; + +void iterate_const(const UNBOUNDED_SET& set) +{ + { + UNBOUNDED_SET_CONST_ITERATOR iterator (set); + while (!iterator.done ()) + { + DATA *data = 0; + iterator.next (data); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%x,%x\n"), + data->alloc_, data->dfunc_)); + + DATA data_second = *iterator; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%x,%x\n"), + data_second.alloc_, data_second.dfunc_)); + + iterator.advance (); + } + } +} + +struct DummyFunctor +{ + int operator() (void) { return 0; } +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Collection_Test")); + + deletion_func NO_DFUNC = (deletion_func)0; + DummyFunctor dummyfunc; + + { + UNBOUNDED_SET unbounded_set; + + unbounded_set.insert (UglyThing ((void*)&unbounded_set, NO_DFUNC)); + unbounded_set.insert (UglyThing ((void*)&dummyfunc, NO_DFUNC)); + + { + for (UNBOUNDED_SET::iterator iterator = unbounded_set.begin (); + iterator != unbounded_set.end (); + ++iterator) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%x,%x\n"), + (*iterator).alloc_, (*iterator).dfunc_)); + } + } + + unbounded_set.insert (UglyThing (0, NO_DFUNC)); + unbounded_set.remove (UglyThing ((void*)&dummyfunc, NO_DFUNC)); + + { + UNBOUNDED_SET_ITERATOR iterator (unbounded_set); + while (!iterator.done ()) + { + DATA *data = 0; + iterator.next (data); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%x,%x\n"), + data->alloc_, data->dfunc_)); + iterator.advance (); + } + } + iterate_const (unbounded_set); + + unbounded_set.reset (); + + { + DATA *data; + UNBOUNDED_SET_ITERATOR i (unbounded_set); + + while (i.next (data) != 0) + { + ACE_DEBUG ((LM_DEBUG, "%x,%x\n", data->alloc_, data->dfunc_)); + i.advance (); + } + } + iterate_const (unbounded_set); + } + + { + ARRAY array; + } + + { + ARRAY array (0); + } + + { + ARRAY array1; + array1.size (2); + array1[0] = 4; + array1[1] = 4; + + ARRAY array2 (2, 4); + + ARRAY array3 (array2); + + ARRAY array4; + array4 = array2; + + ACE_ASSERT (array1 == array2); + ACE_ASSERT (array1 == array3); + ACE_ASSERT (array1 == array4); + + { + for (size_t i = 0; + i != array1.size (); + ++i) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%d\n"), + array1[i])); + ACE_ASSERT (array1[i] == 4); + } + } + + { + ARRAY_ITERATOR iterator (array1); + while (!iterator.done ()) + { + ARRAY_DATA *data = 0; + iterator.next (data); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%d\n"), + (*data))); + ACE_ASSERT (*data == 4); + iterator.advance (); + } + } + } + + + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/Collection_Test.h b/ACE/tests/Collection_Test.h new file mode 100644 index 00000000000..249c9dd281d --- /dev/null +++ b/ACE/tests/Collection_Test.h @@ -0,0 +1,36 @@ +// -*- C++ -*- + +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Collection_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_COLLECTION_TEST_H +#define ACE_TESTS_COLLECTION_TEST_H + +typedef void (*deletion_func)(void* p); + +struct UglyThing +{ + void *alloc_; + deletion_func dfunc_; + + UglyThing (void* alloc = 0, deletion_func dfunc = 0); + bool operator== (const UglyThing& r) const; +}; + +#endif /* ACE_TESTS_COLLECTION_TEST_H */ diff --git a/ACE/tests/Config_Test.cpp b/ACE/tests/Config_Test.cpp new file mode 100644 index 00000000000..01f6c6c5af3 --- /dev/null +++ b/ACE/tests/Config_Test.cpp @@ -0,0 +1,1526 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Config_Test.cpp +// +// = DESCRIPTION +// This is a test that makes sure various classes in +// <ACE_Configuration> work correctly. +// +// = AUTHOR +// Michael Searles <msearles@base16.com>, +// Chris Hafey <chafey@stentor.com>, and +// Jerry D. Odenwelder Jr. <jerry.o@mindspring.com> +// +// ============================================================================ + +#include "test_config.h" +#include "Config_Test.h" +#include "ace/Configuration_Import_Export.h" +#include "ace/OS_NS_ctype.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Config_Test, "$Id$") + +static int +test (ACE_Configuration *config, + ACE_Configuration_Section_Key &testsection) +{ + ACE_TString stvalue; + + // Set some values. + if (config->set_string_value (testsection, + ACE_TEXT ("stvalue"), + ACE_TEXT ("stvaluetest"))) + return -3; + + else if (config->remove_value (testsection, + ACE_TEXT ("stvalue"))) + return -4; + // Make sure it's really gone + else if (0 == config->get_string_value (testsection, + ACE_TEXT ("stvalue"), + stvalue)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("test:remove_value didn't remove\n")), + -4); + + else if (config->set_string_value (testsection, + ACE_TEXT ("stvalue"), + ACE_TEXT ("stvaluetest"))) + return -3; + else if (config->set_string_value (testsection, + ACE_TEXT ("stvalue"), + ACE_TEXT ("second stvaluetest"))) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("test:set_string_value twice failed\n")), + -3); + + else if (config->set_integer_value (testsection, + ACE_TEXT ("intvalue"), + 77)) + return -4; + // Reset to the value we test for below + else if (config->set_integer_value (testsection, + ACE_TEXT ("intvalue"), + 42)) + return -4; + + static size_t const data_len = 80; + u_char data[data_len]; + + for (size_t i = 0; i < data_len ; ++i) + data[i] = static_cast<u_char> (i + 128); + + if (config->set_binary_value (testsection, + ACE_TEXT ("binvalue"), + data, + data_len)) + return -5; + + // Get the values and compare + if (config->get_string_value (testsection, + ACE_TEXT ("stvalue"), + stvalue)) + return -6; + else if (stvalue != ACE_TEXT ("second stvaluetest")) + return -7; + + u_int intvalue; + + if (config->get_integer_value (testsection, + ACE_TEXT ("intvalue"), + intvalue)) + return -8; + else if (intvalue != 42) + return -9; + + u_char *data_out (0); + + { + void *data_tmp = 0; // Workaround for GCC strict aliasing warning. + size_t length = 0; + + if (config->get_binary_value (testsection, + ACE_TEXT ("binvalue"), + data_tmp, + length)) + return -10; + + data_out = reinterpret_cast <u_char *> (data_tmp); + } + + u_char * the_data = static_cast<u_char *> (data_out); + + // compare em + for (size_t j = 0; j < data_len; ++j) + if (the_data[j] != data[j]) + return -11; + + delete [] the_data; + + // Test iteration. + ACE_TString name; + ACE_Configuration::VALUETYPE type; + u_int index = 0; + int found[3] = { 0, 0, 0 }; // One for each expected value + + while (!config->enumerate_values (testsection, + index, + name, + type)) + { + if (name == ACE_TEXT ("stvalue")) + { + if (type != ACE_Configuration::STRING) + return -12; + if (found[0] != 0) + return -12; + found[0] = 1; + } + else if (name == ACE_TEXT ("intvalue")) + { + if (type != ACE_Configuration::INTEGER) + return -13; + if (found[1] != 0) + return -13; + found[1] = 1; + } + else if (name == ACE_TEXT ("binvalue")) + { + if (type != ACE_Configuration::BINARY) + return -14; + if (found[2] != 0) + return -14; + found[2] = 1; + } + index++; + } + + // Make sure we got three values. + if (index != 3 || !found[0] || !found[1] || !found[2]) + return -15; + + { + // Add some subsections. This part is a separate scope to be sure + // the test2, test3, test4 keys are closed before further + // manipulating/deleting the sections further down in the test. + ACE_Configuration_Section_Key test2; + ACE_Configuration_Section_Key test3; + ACE_Configuration_Section_Key test4; + + if (config->open_section (testsection, + ACE_TEXT ("test2"), + 1, + test2)) + return -16; + else if (config->open_section (testsection, + ACE_TEXT ("test3"), + 1, + test3)) + return -17; + else if (config->open_section (testsection, + ACE_TEXT ("test4"), + 1, + test4)) + return -18; + } + + // Test enumerate sections. + index = 0; + found[0] = found[1] = found[2] = 0; + while (!config->enumerate_sections (testsection, + index, + name)) + { + if (name == ACE_TEXT ("test2")) + { + if (found[0] != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("enumerate_sections, dupl test2\n")), + -19); + found[0] = 1; + } + else if (name == ACE_TEXT ("test3")) + { + if (found[1] != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("enumerate_sections, dupl test3\n")), + -19); + found[1] = 1; + } + else if (name == ACE_TEXT ("test4")) + { + if (found[2] != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("enumerate_sections, dupl test4\n")), + -19); + found[2] = 1; + } + index++; + } + + if (index != 3 || !found[0] || !found[1] || !found[2]) + return -19; + + // Remove a subsection + if (config->remove_section (testsection, + ACE_TEXT ("test2"), + 0)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p (%d)\n"), + ACE_TEXT ("remove_section test2"), + ACE_OS::last_error ()), + -20); + + // Try to remove it again + if (!config->remove_section (testsection, + ACE_TEXT ("test2"), + 0)) + return -21; + + return 0; +} + +static int +test (ACE_Configuration *config) +{ + const ACE_Configuration_Section_Key& root = + config->root_section (); + + { + // Scope this so the testsection key is closed before trying to + // remove the "test" section. + ACE_Configuration_Section_Key testsection; + + if (config->open_section (root, + ACE_TEXT ("test"), + 1, + testsection)) + return -2; + + int ret_val = test (config, testsection); + if (ret_val) + return ret_val; + } + + // Try to remove the testsection root, it should fail since it still + // has subkeys + if (!config->remove_section (root, + ACE_TEXT ("test"), + 0)) + return -22; + + { + // Test find section, and be sure the key is closed before testing the + // remove, below. + ACE_Configuration_Section_Key result; + + if (config->open_section (root, + ACE_TEXT ("test"), + 0, + result)) + return -23; + } + + // Now test the recursive remove. + if (config->remove_section (root, + ACE_TEXT ("test"), + 1)) + return -24; + + // Make sure its not there + ACE_Configuration_Section_Key testsectiongone; + if (!config->open_section (root, + ACE_TEXT ("test"), + 0, + testsectiongone)) + return -25; + + return 0; +} + +static int +test_subkey_path (ACE_Configuration* config) +{ + ACE_Configuration_Section_Key root = + config->root_section (); + + ACE_Configuration_Section_Key testsection; + + if (config->open_section (root, + ACE_TEXT ("Software\\ACE\\test"), + 1, + testsection)) + return -26; + + int ret_val = test (config, testsection); + if (ret_val) + return ret_val; + + if (config->open_section (root, + ACE_TEXT ("Software"), + 0, + testsection)) + return -27; + + if (config->remove_section (testsection, + ACE_TEXT ("ACE"), + 1)) + return -28; + + return 0; +} + +static int +run_tests (void) +{ + int status; + + { + // Test import of a legit INI format from a previously-existing file. + ACE_Configuration_Heap cf; + if ((status = cf.open ()) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_Configuration_Heap::open returned %d\n"), + status)); + ACE_Ini_ImpExp import (cf); + // This one should work... + status = import.import_config (ACE_TEXT ("Config_Test_Import_1.ini")); + if (status != 0) { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Config_Test_Import_1.ini failed"))); + } + else { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Config_Test_Import_1.ini imported\n"))); + + // Imported clean; verify content. See ini file for expected content. + // Verify the expected sections are there, but no others. Verify the + // expected keys are there, but no others. + int section1_seen = 0, section2_seen = 0; + int somekey_seen = 0, someotherkey_seen = 0; + int index; + ACE_TString sect_name; + const ACE_Configuration_Section_Key &root = cf.root_section (); + for (index = 0; + (status = cf.enumerate_sections (root, index, sect_name)) == 0; + ++index) { + if (index > 1) // There are only two sections. + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Enumerated %d sections; expected 2\n"), + index + 1)); + else { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Enumerated to section %s\n"), + sect_name.c_str ())); + if (sect_name == ACE_TEXT ("SectionOne")) { + if (section1_seen) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Saw %s multiple times!\n"), + sect_name.c_str ())); + section1_seen = 1; + // Check for values in this section. + ACE_Configuration_Section_Key sect1; + if (cf.open_section (root, sect_name.c_str (), 0, sect1) != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Failed to open section: %s\n"), + sect_name.c_str ())); + else { + int val_index = 0, val_status; + ACE_TString val_name, value; + ACE_Configuration::VALUETYPE val_type; + while ((val_status = + cf.enumerate_values + (sect1, val_index, val_name, val_type)) == 0) { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Enumerated %s, type %d\n"), + val_name.c_str (), + val_type)); + if (val_type != ACE_Configuration::STRING) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected %s to be STRING, but %d\n"), + val_name.c_str (), + val_type)); + if (val_name == ACE_TEXT ("SomeKey")) { + if (somekey_seen) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Saw %s more than once\n"), + val_name.c_str ())); + somekey_seen = 1; + } + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Unexpected key %s\n"), + val_name.c_str ())); + if ((val_status = cf.get_string_value + (sect1, val_name.c_str (), value)) != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Can't get value of %s\n"), + val_name.c_str ())); + else { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%s value: %s\n"), + val_name.c_str (), value.c_str ())); + if (value != ACE_TEXT ("SomeValue")) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("SomeKey: %s; expected SomeValue\n"))); + } + } + ++val_index; + } + if (val_status == 1) { + if (val_index != 1) // Should have only seen 1 + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected 1 value; saw %d\n"), + index)); + } + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error enumerating %s; status %d\n"), + sect_name.c_str (), val_status)); + } + } + else if (sect_name == ACE_TEXT ("SectionTwo")) { + if (section2_seen) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Saw %s multiple times!\n"), + sect_name.c_str ())); + section2_seen = 1; + // Check for values in this section. + ACE_Configuration_Section_Key sect2; + if (cf.open_section (root, sect_name.c_str (), 0, sect2) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed to open section: %s\n"), + sect_name.c_str ())); + else { + int val_index = 0, val_status; + ACE_TString val_name, value; + ACE_Configuration::VALUETYPE val_type; + while ((val_status = cf.enumerate_values + (sect2, val_index, val_name, val_type)) == 0) { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Enumerated %s, type %d\n"), + val_name.c_str (), + val_type)); + if (val_type != ACE_Configuration::STRING) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected %s to be STRING, but %d\n"), + val_name.c_str (), val_type)); + if (val_name == ACE_TEXT ("SomeOtherKey")) { + if (someotherkey_seen) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Saw %s more than once\n"), + val_name.c_str ())); + someotherkey_seen = 1; + } + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Unexpected key %s\n"), + val_name.c_str ())); + if ((val_status = cf.get_string_value + (sect2, val_name.c_str (), value)) != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Can't get value of %s\n"), + val_name.c_str ())); + else { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%s value: %s\n"), + val_name.c_str (), value.c_str ())); + if (value != ACE_TEXT ("SomeOtherValue")) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("SomeOtherKey: %s; expected SomeOtherValue\n"))); + } + } + ++val_index; + } + if (val_status == 1) { + if (val_index != 1) // Should have only seen 1 + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected 1 value; saw %d\n"), + index)); + } + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error enumerating %s; status %d\n"), + sect_name.c_str (), val_status)); + } + } + else { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Saw unexpected section: %s\n"), + sect_name.c_str ())); + } + } + } + if (status == 1) { // Ran out of sections + if (index != 2) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Saw %d sections; expected 2\n"), + index)); + } + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error enumerating sections; status %d\n"), + status)); + } + } + +#if defined (ACE_WIN32) && !defined (ACE_LACKS_WIN32_REGISTRY) + { + ACE_Configuration_Win32Registry RegConfig (HKEY_LOCAL_MACHINE); + int result = test_subkey_path (&RegConfig); + if (result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Win32Registry test HKEY_LOCAL_MACHINE") + ACE_TEXT (" failed (%d)\n"), result), + -1); + } + // test win32 registry implementation. + HKEY root = + ACE_Configuration_Win32Registry::resolve_key (HKEY_LOCAL_MACHINE, + ACE_TEXT ("Software\\ACE\\test")); + if (!root) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("resolve_key is broken\n")), -2); + + // test resolving of forward slashes + HKEY root_fs = + ACE_Configuration_Win32Registry::resolve_key (HKEY_LOCAL_MACHINE, + ACE_TEXT ("Software/ACE/test"), 0); + if (!root_fs) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("resolve_key resolving slashes is broken\n")), + -2); + + ACE_Configuration_Win32Registry RegConfig (root); + { + int result = test (&RegConfig); + if (result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Win32 registry test root failed (%d)\n"), + result), + -1); + } + +#endif /* ACE_WIN32 && !ACE_LACKS_WIN32_REGISTRY */ + + // Test Heap version + ACE_Configuration_Heap heap_config; + + if (heap_config.open ()) + return 0; + { + int result = test_subkey_path (&heap_config); + if (result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Heap Config subkey test failed (%d)\n"), + result), + -1); + } + + { + int result = test (&heap_config); + if (result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Heap Configuration test failed (%d)\n"), + result), + -1); + } + +#if !defined (ACE_LACKS_MMAP) + // Test persistent heap version + ACE_OS::unlink (ACE_TEXT ("test.reg")); + ACE_Configuration_Heap pers_config; + + if (pers_config.open (ACE_TEXT ("test.reg"))) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Cannot open test.reg\n")), + -1); + + { + int result = test (&pers_config); + if (result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Persistent Heap Config test failed (%d)\n"), + result), + -1); + } +#endif /* !ACE_LACKS_MMAP */ + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test passed\n"))); + return 0; +} + +static int +build_config_object (ACE_Configuration& cfg) +{ + ACE_Configuration_Section_Key root = cfg.root_section (); + ACE_Configuration_Section_Key NetworkSection; + ACE_Configuration_Section_Key LoggerSection; + ACE_Configuration_Section_Key BinarySection; + + if (cfg.open_section (root, + ACE_TEXT ("network"), + 1, + NetworkSection)) + return -1; + + if (cfg.set_integer_value (NetworkSection, + ACE_TEXT ("TimeToLive"), + 100)) + return -2; + else if (cfg.set_string_value (NetworkSection, + ACE_TEXT ("Delay"), + ACE_TString (ACE_TEXT ("FALSE")))) + return -3; + else if (cfg.set_string_value (NetworkSection, + ACE_TEXT ("DestIPAddress"), + ACE_TString (ACE_TEXT ("localhost")))) + return -4; + else if (cfg.set_integer_value (NetworkSection, + ACE_TEXT ("DestPort"), + 12670)) + return -5; + else if (cfg.set_integer_value (NetworkSection, + ACE_TEXT ("ReconnectInterval"), + 3)) + return -6; + + if (cfg.open_section (root, + ACE_TEXT ("logger"), + 1, + LoggerSection)) + return -7; + + if (cfg.set_string_value (LoggerSection, + ACE_TEXT ("Heading"), + ACE_TString (ACE_TEXT ("ACE - Adaptive Communication Environment")))) + return -8; + else if (cfg.set_integer_value (LoggerSection, + ACE_TEXT ("SeekIndex"), + 14)) + return -9; + else if (cfg.set_integer_value (LoggerSection, + ACE_TEXT ("TraceLevel"), + 6)) + return -10; + else if (cfg.set_string_value (LoggerSection, + ACE_TEXT ("Justification"), + ACE_TString (ACE_TEXT ("left_justified")))) + return -11; + else if (cfg.set_string_value (LoggerSection, + ACE_TEXT ("LogFilePath"), + ACE_TString (ACE_TEXT ("log/")))) + return -12; + else if (cfg.set_string_value (LoggerSection, + ACE_TEXT ("TransactionFilePath"), + ACE_TString (ACE_TEXT ("data/")))) + return -13; + + if (cfg.open_section (root, + ACE_TEXT ("binary"), + 1, + BinarySection)) + return -14; + + u_char data[80]; + + for (int i = 0; i < 80; i++) + data[i] = i + 128; + + if (cfg.set_binary_value (BinarySection, + ACE_TEXT ("data"), + data, + 80)) + return -15; + + ACE_TString string((ACE_TCHAR*) 0);// = '0'; + // Try to set the unnamed, default value. + if (cfg.set_string_value (LoggerSection, + 0,//string.c_str (),//0, //ACE_TEXT ("x"), + ACE_TString (ACE_TEXT ("some string")))) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("could not set value with null name\n")), + -16); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("here\n"))); + //return 0; + //ACE_TString string; + ACE_TString name ((ACE_TCHAR*)0); + if (cfg.get_string_value (LoggerSection, + name.c_str (), //0, //ACE_TEXT ("x"), + string)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("error using ACE_TString::c_str() == %d, len == %d\n"), + name.c_str(), name.length ()), + -17); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("the value for the unnamed var=(%s)\n"), + string.c_str ())); + + return 0; +} + +/* + * Test ACE_Configuration::operator== + */ +int +Config_Test::testEquality () +{ + // create and open 2 ACE_Configuration objects. + ACE_Configuration_Heap heap1; + ACE_Configuration_Heap heap2; + if ((heap1.open ()) != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Cannot open heap1\n")), + -1); + + } + else if ((heap2.open ()) != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Cannot open heap2\n")), + -1); + } + + // populate them equally + if (build_config_object (heap1) != 0 || + build_config_object (heap2) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("could not build config object\n")), + -1); + + // test equality + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The objects should equal...\n"))); + if (heap1 == heap2) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("And they do ;-)\n"))); + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("And they do not :-(\n") + ACE_TEXT ("operator== Failed when objects equal\n")), + -1); + } + + // add a section and value to heap1 + ACE_Configuration_Section_Key root1 = heap1.root_section (); + ACE_Configuration_Section_Key NewSection; + if (heap1.open_section (root1, + ACE_TEXT ("NewSection"), + 1, + NewSection)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding section to heap1\n")), + -1); + else if (heap1.set_integer_value (NewSection, + ACE_TEXT ("TestIntValue"), + 100)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding value to heap1\n")), + -2); + + // test equality + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The objects should NOT equal...\n"))); + if (heap1 == heap2) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("They Do :-(\noperator== Failed ") + ACE_TEXT ("when lhs contains data not in rhs\n")), + -1); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("And they do not ;-)\n"))); + + // + // add same section to heap2 + // + ACE_Configuration_Section_Key root2 = heap2.root_section (); + ACE_Configuration_Section_Key NewSection2; + if (heap2.open_section (root2, + ACE_TEXT ("NewSection"), + 1, + NewSection2)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding section to heap2\n")), + -1); + else if (heap2.set_integer_value (NewSection2, + ACE_TEXT ("TestIntValue"), + 100)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding value to heap2\n")), + -2); + else if (heap2.set_integer_value (NewSection2, + ACE_TEXT ("TestIntValue2"), + 100)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding second value to heap2\n")), + -2); + + // test equality + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The objects should NOT equal...\n"))); + if (heap1 == heap2) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("And They Do :-(\noperator== Failed ") + ACE_TEXT ("when rhs contains value not in lhs\n")), + -1); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("And they do not ;-)\n"))); + + // add new value in heap 1 + if (heap1.set_integer_value (NewSection, + ACE_TEXT ("TestIntValue2"), + 100)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding second value to heap1\n")), + -2); + + // test equality + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The objects should be equal...\n"))); + if (heap1 == heap2) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("And they are ;-)\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("And they are not :-(\n") + ACE_TEXT ("operator== Failed\n")), + -1); + + // Add a new section to heap2 + ACE_Configuration_Section_Key AnotherNewSection2; + if (heap2.open_section (root2, + ACE_TEXT ("AnotherNewSection"), + 1, + AnotherNewSection2)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding second section to heap2\n")), + -1); + else if (heap2.set_integer_value (AnotherNewSection2, + ACE_TEXT ("TestIntValue"), + 100)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding value in second section") + ACE_TEXT (" to heap2\n")), + -2); + + // test equality + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The objects should NOT equal...\n"))); + if (heap1 == heap2) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("And they do :-(\noperator== Failed ") + ACE_TEXT ("when rhs contains data not in lhs\n")), + -1); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("And they do not :-)\n"))); + + // add section back to heap1 + ACE_Configuration_Section_Key AnotherNewSection1; + if (heap1.open_section (root1, + ACE_TEXT ("AnotherNewSection"), + 1, + AnotherNewSection1)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding second section to heap1\n")), + -1); + else if (heap1.set_integer_value (AnotherNewSection1, + ACE_TEXT ("TestIntValue"), + 100)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error adding second value to second ") + ACE_TEXT ("section in heap1\n")), + -2); + + // test equality + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The objects should be equal...\n"))); + if (heap1 == heap2) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("And they are ;-)\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("And they are not :-(\n") + ACE_TEXT ("operator== Failed\n")), + -1); + + this->equality_tested_ = 1; + return 0; +} + +/* + * Compare INI import data in fromFile to origional data exported (in origional) + * + * This compare is destructive to the origional object. + * I realize that normally you would not do such an obscene thing but + * this funciton has a special purpose and I know my origional is not needed + * after calling this routine. + * This is done because configuration objects that are imported using the INI + * import store all data as strings. My origional has type information and I need to + * know if the import worked. +*/ +static int +iniCompare (ACE_Configuration_Heap& fromFile, ACE_Configuration_Heap& original) +{ + bool rc = true; // start by guessing they are equal + + int sectionIndex = 0; + ACE_TString sectionName; + + const ACE_Configuration_Section_Key& fromFileRoot = fromFile.root_section (); + const ACE_Configuration_Section_Key& originalRoot = original.root_section (); + ACE_Configuration_Section_Key originalSection; + ACE_Configuration_Section_Key fromFileSection; + + // loop through each section in the fromFile object + while ((rc) && + (!fromFile.enumerate_sections (fromFileRoot, + sectionIndex, + sectionName)) ) + { + // find that section in the original object + if (original.open_section (originalRoot, + sectionName.c_str (), + 0, + originalSection) != 0) + // If the original object does not contain the section then we + // are not equal. + rc = false; + else if (fromFile.open_section (fromFileRoot, + sectionName.c_str (), + 0, + fromFileSection) != 0) + // if there is some error opening the section in the fromFile + rc = false; + else + { + // Well the sections match + int valueIndex = 0; + ACE_TString valueName; + ACE_Configuration::VALUETYPE valueType; + ACE_Configuration::VALUETYPE originalType; + + // Enumerate each value in the fromFile section + while ((rc) && + (!fromFile.enumerate_values (fromFileSection, + valueIndex, + valueName, + valueType))) + { + // look for the same value in the original section + if (original.find_value (originalSection, + valueName.c_str (), + originalType) != 0) + // We're not equal if the same value cannot be found + // in the original object. + rc = false; + else + { + ACE_TString fromFileString, originalString; + + + if (fromFile.get_string_value (fromFileSection, + valueName.c_str (), + fromFileString) != 0) + // we're not equal if we cannot get this string + rc = false; + else if (originalType != ACE_Configuration::STRING) // If the original type is not a string + { + // convert original data to a string. + + if (originalType == ACE_Configuration::INTEGER) + { + u_int intValue; + ACE_TCHAR int_value[32]; + + if (original.get_integer_value (originalSection, + valueName.c_str (), + intValue) != 0) + // we're not equal if we cannot get rhs int + rc = false; + + ACE_OS::sprintf (int_value, ACE_TEXT ("%08x"), intValue); + originalString = int_value; + } + else if (originalType == ACE_Configuration::BINARY) + { + void* binary_data; + size_t binary_length; + + if (original.get_binary_value (originalSection, + valueName.c_str (), + binary_data, + binary_length)) + // we're not equal if we cannot get this string + rc = false; + else + { + ACE_TCHAR bin_value[3]; + + unsigned char* ptr = (unsigned char*)binary_data; + while (binary_length) + { + if (ptr != binary_data) + originalString += ACE_TEXT (","); + + ACE_OS::sprintf (bin_value, + ACE_TEXT ("%02x"), + *ptr); + originalString += bin_value; + --binary_length; + ++ptr; + } + delete [] (char *)binary_data; + }// end successful binary read + }// end if originalType was binary + else + // if the type is invalid, then go ahead and fail it. + rc = false; + + }// end if the original type was not a string. + else + { + if (original.get_string_value (originalSection, + valueName.c_str (), + originalString) != 0) + { + // we're not equal if we cannot get rhs string + rc = false; + } + + } + + rc &= fromFileString == originalString; + + if (rc) + // before we move on remove this value from the original. + original.remove_value (originalSection, + valueName.c_str ()); + + }// end else if values match. + + valueIndex++; + + }// end value while loop + + // at this point the original should have no values. look + // for values in the original section + valueIndex = 0; + while ((rc) && + (!original.enumerate_values (originalSection, + valueIndex, + valueName, + originalType))) + valueIndex++; + + // having a value indicates a mismatch + rc = valueIndex == 0; + + }// end else if sections match. + + if (rc) + // before we move on remove the section from the original. + original.remove_section (originalRoot, + sectionName.c_str (), + 0); // do not remove subsections. + + sectionIndex++; + + }// end section while loop + + // Finally, if the original has any sections, then we're not equal + sectionIndex = 0; + while ((rc) && + (!original.enumerate_sections (originalRoot, + sectionIndex, + sectionName))) + sectionIndex++; + + rc = sectionIndex == 0; + + return rc; +} + +// change a network section value +int Config_Test::change_one (ACE_Configuration &cfg, u_int a) +{ + ACE_Configuration_Section_Key root = cfg.root_section (); + ACE_Configuration_Section_Key NetworkSection; + ACE_Configuration_Section_Key LoggerSection; + ACE_Configuration_Section_Key BinarySection; + + if (cfg.open_section (root, + ACE_TEXT ("network"), + 1, + NetworkSection)) + return -1; + + if (cfg.set_integer_value (NetworkSection, + ACE_TEXT ("TimeToLive"), + a)) + return -2; + + return 0; +} + +// Used to test INI Import Export class + +int +Config_Test::testIniFormat () +{ + int rc = 0; + if (!this->equality_tested_) + { + rc = this->testEquality (); + if (rc != 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Equality Test Failed\n"))); + return rc; + } + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing INI Format Import/Export\n"))); + ACE_Configuration_Heap fromFile; + + // 1. Creates an ACE_Configuration_Heap object + ACE_Configuration_Heap original; + + rc = original.open (); + if (rc == 0) + { + rc = build_config_object (original); + // 2. Calls build_config_object to populate + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error populating original config ") + ACE_TEXT ("object (%d)\n"), + rc), + -1); + // 3. Export + ACE_Ini_ImpExp importExport (original); + + rc = importExport.export_config (ACE_TEXT ("testConfig.ini")); + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error Exporting (%d)\n"), + rc), + -1); + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Could not open original object (%d)\n"), + rc), + -1); + + // At this point we've successfully created, populated and written + // the configuration object + // 5. Creates a new ACE_Configuration_Heap object + rc = fromFile.open (); + if (rc == 0) + { + // 6. Imports + ACE_Ini_ImpExp importExport (fromFile); + + rc = importExport.import_config (ACE_TEXT ("testConfig.ini")); + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error Exporting (%d)\n"), + rc), + -1); + + // 7. Compares to original. + // This is a special compare since files imported using the + // INI file import do not contain type information + // + // NOTE: After this call the original object will be invalid!!! + // + if (!iniCompare (fromFile, original)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Object read from file does not ") + ACE_TEXT ("equal original (%d)\n"), + rc), + -1); + }// end if heap could not be opened. + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Could not open fromFile object (%d)\n"), + rc), + -1); + + // 8. Calls old "read_config" methods on the new object + + int nTimeToLive; + int bDelay; + int nDestPort; + int nReconnectInterval; + int nTraceLevel; + + ACE_TCHAR pszDestIPAddress[TEST_MAX_STRING]; + ACE_TCHAR pszLogFilePath[TEST_MAX_STRING]; + ACE_TCHAR pszTransactionFilePath[TEST_MAX_STRING]; + ACE_TCHAR pszHeading[TEST_MAX_STRING]; + ACE_TCHAR pszJustification[TEST_MAX_STRING]; + + ACE_Configuration_Section_Key root = fromFile.root_section (); + + // Process [network] section + ACE_Configuration_Section_Key NetworkSection; + if (fromFile.open_section (root, + ACE_TEXT ("network"), + 1, + NetworkSection) == 0) + { + this->get_section_integer (fromFile, + NetworkSection, + ACE_TEXT ("TimeToLive"), + &nTimeToLive, + 1, + 20); + + this->get_section_boolean (fromFile, + NetworkSection, + ACE_TEXT ("Delay"), + &bDelay); + + this->get_section_string (fromFile, + NetworkSection, + ACE_TEXT ("DestIPAddress"), + pszDestIPAddress, + TEST_MAX_STRING); + + this->get_section_integer (fromFile, + NetworkSection, + ACE_TEXT ("DestPort"), + &nDestPort, + 0, + 65535); + + this->get_section_integer (fromFile, + NetworkSection, + ACE_TEXT ("ReconnectInterval"), + &nReconnectInterval, + 0, + 65535); + }// end of "network" section + + // Process [logger] section + ACE_Configuration_Section_Key LoggerSection; + if (fromFile.open_section (root, + ACE_TEXT ("logger"), + 1, + LoggerSection) == 0) + { + this->get_section_string (fromFile, + LoggerSection, + ACE_TEXT ("Heading"), + pszHeading, + TEST_MAX_STRING); + this->get_section_integer (fromFile, + LoggerSection, + ACE_TEXT ("TraceLevel"), + &nTraceLevel, + 1, + 20); + this->get_section_string (fromFile, + LoggerSection, + ACE_TEXT ("Justification"), + pszJustification, + TEST_MAX_STRING); + this->get_section_string (fromFile, + LoggerSection, + ACE_TEXT ("LogFilePath"), + pszLogFilePath, + TEST_MAX_STRING); + this->get_section_string (fromFile, + LoggerSection, + ACE_TEXT ("TransactionFilePath"), + pszTransactionFilePath, + TEST_MAX_STRING); + }// end of "logger" section + + if (!rc) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("INI Format Import/Export Works ;-)\n"))); + return rc; +} + +// Used to test registry Import Export class + +int +Config_Test::testRegFormat () +{ + int rc = 0; + if (!this->equality_tested_) + { + rc = this->testEquality (); + if (rc != 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Equality Test Failed\n"))); + return rc; + } + + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing Registry Format Import/Export\n"))); + ACE_Configuration_Heap fromFile; + + // 1. Creates an ACE_Configuration_Heap object + ACE_Configuration_Heap original; + + rc = original.open (); + if (rc == 0) + { + // 2. Calls build_config_object to populate + rc = build_config_object (original); + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error populating original config ") + ACE_TEXT ("object (%d)\n"), + rc), + -1); + // 3. Export + ACE_Registry_ImpExp importExport (original); + + rc = importExport.export_config (ACE_TEXT ("testConfig.ini")); + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error Exporting (%d)\n"), + rc), + -1); + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Could not open original object (%d)\n"), + rc), + -1); + + // At this point we've successfully created, populated and written + // the configuration object + // 5. Creates a new ACE_Configuration_Heap object + rc = fromFile.open (); + if (rc == 0) + { + // 6. Imports + ACE_Registry_ImpExp importExport (fromFile); + + rc = importExport.import_config (ACE_TEXT ("testConfig.ini")); + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("Error Exporting (%d)\n"), + rc), + -1); + + // 7. Compares to original. + if (fromFile != original) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Object read from file does not ") + ACE_TEXT ("equal original (%d)\n"), + rc), + -1); + + // 7.1 Change a value and test NOT equal case + change_one (original, 101); + if (fromFile == original) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("not pass value change test (%d)\n"), + rc), + -1); + } + + // 7.2 change value back, they should be equal now + change_one (original, 100); + if (fromFile != original) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("not pass value change test (%d)\n"), + rc), + -1); + } + + }// end if heap could not be opened. + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Could not open fromFile object (%d)\n"), + rc), + -1); + + if (!rc) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Registry Format Import/Export Works ;-)\n"))); + return rc; +} + + +// Reads a string value from a configuration object. + +void +Config_Test::get_section_string (ACE_Configuration& config, + ACE_Configuration_Section_Key& SectionKey, + const ACE_TCHAR* pszName, + ACE_TCHAR* pszVariable, + int nMaxLength) +{ + ACE_TString StringValue; + + if (config.get_string_value (SectionKey, + pszName, + StringValue) == 0) + { + ACE_OS::strncpy (pszVariable, + StringValue.c_str (), + nMaxLength); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + pszName, + pszVariable)); + } +} + +// Reads an integer value from a configuration object (when it's +// stored as a string) +void +Config_Test::get_section_integer (ACE_Configuration& config, + ACE_Configuration_Section_Key& SectionKey, + const ACE_TCHAR* pszName, + int* nVariable, + int nMinValue, + int nMaxValue) +{ + ACE_TString StringValue; + ACE_TCHAR pszString[30]; + ACE_OS::strcpy (pszString, ACE_TEXT ("0")); + int IntegerValue = 0; + + if (config.get_string_value (SectionKey, + pszName, + StringValue) == 0) + { + ACE_OS::strncpy (pszString, + StringValue.c_str (), + 30); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + pszName, + pszString)); + } + + // convert to integer + IntegerValue = ACE_OS::atoi (pszString); + IntegerValue = (IntegerValue < nMinValue) ? nMinValue : IntegerValue; + IntegerValue = (IntegerValue > nMaxValue) ? nMaxValue : IntegerValue; + *nVariable = IntegerValue; +} + +// Reads a boolean value from a configuration object (when it's stored as a string). + +void +Config_Test::get_section_boolean (ACE_Configuration& config, + ACE_Configuration_Section_Key& SectionKey, + const ACE_TCHAR* pszName, + int* pVariable) +{ + ACE_TString StringValue; + ACE_TCHAR pszString[10]; + ACE_OS::strcpy (pszString, ACE_TEXT ("0")); + + if (config.get_string_value (SectionKey, + pszName, + StringValue) == 0) + { + ACE_OS::strncpy (pszString, + StringValue.c_str (), + 10); + for (ACE_TCHAR* pSrc = pszString; + *pSrc != ACE_TEXT ('\0'); + pSrc++) + // Convert to uppercase + if (ACE_OS::ace_islower (*pSrc)) + *pSrc = ACE_OS::ace_tolower (*pSrc); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + pszName, + pszString)); + + if (ACE_OS::strcmp (pszString, + ACE_TEXT ("TRUE")) == 0) + *pVariable = 1; + else if (ACE_OS::strcmp (pszString, + ACE_TEXT ("FALSE")) == 0) + *pVariable = 0; + } +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Config_Test")); + int errors = 0; + Config_Test manager; + + if ((errors += manager.testEquality ()) != 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Failed the equality Test\n"))); + + if ((errors += manager.testRegFormat ()) != 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Failed the REG Format Test\n"))); + + if ((errors += manager.testIniFormat ()) != 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Failed the INI Format Test\n"))); + + if ((errors += run_tests ()) != 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Failed in run_tests\n"))); + + ACE_END_TEST; + return errors == 0 ? 0 : 1; +} diff --git a/ACE/tests/Config_Test.h b/ACE/tests/Config_Test.h new file mode 100644 index 00000000000..28fcdbd2d36 --- /dev/null +++ b/ACE/tests/Config_Test.h @@ -0,0 +1,77 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// ConfigTest.h +// +// = DESCRIPTION +// This is a test that makes sure the <import_config_as_strings> of +// <ACE_Configuration_Heap> class works correctly. +// +// = AUTHOR +// Michael Searles <msearles@base16.com> and +// Jerry D. Odenwelder Jr. <jerry.o@mindspring.com> +// +// ============================================================================ + +#ifndef __CONFIG_TEST_H +#define __CONFIG_TEST_H + +#include "ace/Configuration.h" + +const int TEST_MAX_STRING = 256; + +class Config_Test +{ +public: + Config_Test (void): equality_tested_ (0) { } + ~Config_Test (void) { } + + // Used to test the equality and inequality operations. + int testEquality (); + + // Used to test INI Import Export class + int testIniFormat (); + + // Used to test Regiastry Import Export class + // 1. Creates an ACE_Configuration_Heap object + // 2. Calls buildConfigObject to populate + // 3. Exports + // 5. Creates a new ACE_Configuration_Heap object + // 6. Imports + // 7. Compares to origional. + // 8. Clean-up + int testRegFormat (); + + +private: + // change a value for value change test + int change_one (ACE_Configuration &l, u_int a = 101); + + void get_section_string (ACE_Configuration& config, + ACE_Configuration_Section_Key& SectionKey, + const ACE_TCHAR* pszName, + ACE_TCHAR* pszVariable, + int nMaxLength); + + void get_section_integer (ACE_Configuration& config, + ACE_Configuration_Section_Key& SectionKey, + const ACE_TCHAR* pszName, + int* nVariable, + int nMinValue, + int nMaxValue); + + void get_section_boolean (ACE_Configuration& config, + ACE_Configuration_Section_Key& SectionKey, + const ACE_TCHAR* pszName, + int* pVariable); + +private: + int equality_tested_; // make sure the equality operator works before proceeding. +}; + +#endif /* __CONFIG_TEST_H */ diff --git a/ACE/tests/Config_Test.ini b/ACE/tests/Config_Test.ini new file mode 100644 index 00000000000..db3df20979f --- /dev/null +++ b/ACE/tests/Config_Test.ini @@ -0,0 +1,22 @@ +# -------------------------------------------------------------------- +# Example test.ini file for import_config_as_strings of the +# <ACE_Configuration_Heap> class +# -------------------------------------------------------------------- +# Use whitespace (tabs and spaces) freely. +# String to the left of the equal sign can be delimited with double +# quotes. +# -------------------------------------------------------------------- +[network] + TimeToLive = 100 + Delay = FALSE + DestIPAddress = localhost + DestPort = 12670 + ReconnectInterval = 3 + +[logger] + Heading = "ACE - Adaptive Communication Environment" + SeekIndex = 14 + TraceLevel = 6 # Can comment lines like this + Justification = left_justified + LogFilePath = log/ + TransactionFilePath = data/ diff --git a/ACE/tests/Config_Test_Import_1.ini b/ACE/tests/Config_Test_Import_1.ini new file mode 100644 index 00000000000..d3194658cdb --- /dev/null +++ b/ACE/tests/Config_Test_Import_1.ini @@ -0,0 +1,5 @@ +[SectionOne] +SomeKey=SomeValue + +[SectionTwo] +SomeOtherKey=SomeOtherValue diff --git a/ACE/tests/Conn_Test.cpp b/ACE/tests/Conn_Test.cpp new file mode 100644 index 00000000000..6f174583ea9 --- /dev/null +++ b/ACE/tests/Conn_Test.cpp @@ -0,0 +1,784 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Conn_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_Acceptor> and <ACE_Connector> +// classes. The test forks processes or spawns threads (depending +// upon the platform) and then executes client and server allowing +// them to connect and exchange data. The test also illustrates +// how the <ACE_Strategy_Connector> works by showing how you can +// cache connections on the client. +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, +// Chris Cleeland <cleeland@cs.wustl.edu>, +// and Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/SOCK_Connector.h" +#include "ace/LOCK_SOCK_Acceptor.h" +#include "ace/Acceptor.h" +#include "ace/Handle_Set.h" +#include "ace/Connector.h" +#include "ace/Auto_Ptr.h" +#include "ace/Get_Opt.h" +#include "ace/Process_Mutex.h" +#include "ace/Signal.h" +#include "Conn_Test.h" +#include "ace/Barrier.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" +#include "ace/os_include/os_netdb.h" + +ACE_RCSID(tests, Conn_Test, "$Id$") + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// This test doesn't work well using fork() on MacOS X. So we +// will force it to use threads instead. +#if defined (__APPLE__) +# define ACE_LACKS_FORK +#endif /* __APPLE__ */ + +// The following works around bugs with some operating systems, which +// don't allow multiple threads/process to call accept() on the same +// listen-mode port/socket. Also, note that since timed accept is +// implemented using select(), and we use timed accepts with threads, +// we need a real lock when using timed accepts even if the OS has +// thread-safe accept. +// +#if defined (ACE_LACKS_FORK) +# if defined (ACE_HAS_THREADS) +# include "ace/Thread_Mutex.h" + typedef ACE_Thread_Mutex ACCEPTOR_LOCKING; +# else +# include "ace/Null_Mutex.h" + typedef ACE_Null_Mutex ACCEPTOR_LOCKING; +# endif /* ACE_HAS_THREADS */ +#else +# if defined (ACE_HAS_THREAD_SAFE_ACCEPT) +# include "ace/Null_Mutex.h" + typedef ACE_Null_Mutex ACCEPTOR_LOCKING; +# else +# include "ace/Process_Mutex.h" + typedef ACE_Process_Mutex ACCEPTOR_LOCKING; +# endif /* ACE_HAS_THREAD_SAFE_ACCEPT */ +#endif /* ACE_LACKS_FORK */ + +#if defined (ACE_HAS_TEMPLATE_TYPEDEFS) +#define LOCK_SOCK_ACCEPTOR ACE_LOCK_SOCK_Acceptor<ACCEPTOR_LOCKING> +#else +#define LOCK_SOCK_ACCEPTOR ACE_LOCK_SOCK_Acceptor<ACCEPTOR_LOCKING>, ACE_INET_Addr +#endif /* ACE_HAS_TEMPLATE_TYPEDEFS */ + +typedef ACE_Oneshot_Acceptor<Svc_Handler, + LOCK_SOCK_ACCEPTOR> + ACCEPTOR; +typedef ACE_Connector<Svc_Handler, + ACE_SOCK_CONNECTOR> + CONNECTOR; +typedef ACE_Strategy_Connector<Svc_Handler, + ACE_SOCK_CONNECTOR> + STRAT_CONNECTOR; +typedef ACE_NOOP_Creation_Strategy<Svc_Handler> + NULL_CREATION_STRATEGY; +typedef ACE_NOOP_Concurrency_Strategy<Svc_Handler> + NULL_ACTIVATION_STRATEGY; +typedef ACE_Cached_Connect_Strategy<Svc_Handler, + ACE_SOCK_CONNECTOR, + ACE_SYNCH_MUTEX> + CACHED_CONNECT_STRATEGY; + +#define CACHED_CONNECT_STRATEGY ACE_Cached_Connect_Strategy<Svc_Handler, ACE_SOCK_CONNECTOR, ACE_SYNCH_MUTEX> +#define REFCOUNTED_HASH_RECYCLABLE_ADDR ACE_Refcounted_Hash_Recyclable<ACE_INET_Addr> + +// Default number of clients/servers. +#if defined (ACE_HAS_PHARLAP) +// PharLap is, by default, resource contrained. Test for something that works +// on the default configuration. +static int n_servers = 2; +static int n_clients = 4; +#else +static int n_servers = 5; +static int n_clients = 5; +#endif /* ACE_HAS_PHARLAP */ + +static int n_client_iterations = 3; + +Svc_Handler::Svc_Handler (ACE_Thread_Manager *) +{ +} + +int +Svc_Handler::open (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) opening Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + // Enable non-blocking I/O. + if (this->peer ().enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("enable")), + -1); + return 0; +} + +int +Svc_Handler::recycle (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) recycling Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + return 0; +} + +void +Svc_Handler::send_data (void) +{ + // Send data to server. + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + if (this->peer ().send_n (c, 1) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("send_n"))); +} + +void +Svc_Handler::recv_data (void) +{ + ACE_SOCK_Stream &new_stream = this->peer (); + + ACE_Handle_Set handle_set; + handle_set.set_bit (new_stream.get_handle ()); + + const char *t = ACE_ALPHABET; + + // Read data from client (terminate on error). + + for (ssize_t r_bytes; ;) + { + // Since we're in non-blocking mode we need to use <select> to + // avoid busy waiting. +#if defined (ACE_WIN64) + int select_width = 0; +#else + int select_width = int (new_stream.get_handle ()) + 1; +#endif /* ACE_WIN64 */ + if (ACE_OS::select (select_width, handle_set) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select"))); + else + { + char c; + + while ((r_bytes = new_stream.recv (&c, 1)) > 0) + { + ACE_ASSERT (*t == c); + + // We need to guard against cached connections, which + // will send multiple sequences of letters from 'a' -> + // 'z' through the same connection. + if (*t == 'z') + t = ACE_ALPHABET; + else + t++; + } + + if (r_bytes == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reached end of input, connection closed by client\n"))); + + // Close endpoint handle (but don't close <this> yet + // since we're going to recycle it for the next + // iteration). + if (new_stream.close () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("close"))); + break; + } + else if (r_bytes == -1) + { + if (errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) no input available, going back to reading\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("recv_n"))); + } + } + } +} + +int +Svc_Handler::close (u_long side) +{ + // Only run this protocol if we're the write-side (i.e., "1"). + if (side == 1 && this->peer ().close () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("close_writer"))); + // Trigger the shutdown. + return this->handle_close (); +} + +int +Svc_Handler::idle (u_long flags) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) idling Svc_Handler %@ with handle %d\n"), + this, + this->peer ().get_handle ())); + return ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>::idle (flags); +} + +struct Client_Info + // = TITLE + // Information passed to the client so it can communicate with the + // server. +{ + ACE_INET_Addr *server_addr_; + // Address of the server to connect with. + + CONNECTOR *connector_; + // Connection factory. + + STRAT_CONNECTOR *strat_connector_; + // Strategy for connecting. + +#if defined (ACE_HAS_THREADS) + ACE_Barrier *barrier_; + // Performs barrier synchronization. +#endif /* ACE_HAS_THREADS */ +}; + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +static void +timed_blocking_connect (CONNECTOR &con, + const ACE_INET_Addr &server_addr) +{ + ACE_Time_Value tv (ACE_DEFAULT_TIMEOUT); + ACE_Synch_Options options (ACE_Synch_Options::USE_TIMEOUT, tv); + + Svc_Handler *svc_handler; + ACE_NEW (svc_handler, + Svc_Handler); + + // Perform a timed-blocking connect to the server (this should + // connect quickly since we're in the same address space or same + // host). + if (con.connect (svc_handler, + server_addr, + options) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + else + { + // Send the data to the server. + svc_handler->send_data (); + + // Close the connection completely. + if (svc_handler->close (1) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("close"))); + } +} + +static void +blocking_connect (CONNECTOR &con, + const ACE_INET_Addr &server_addr) +{ + Svc_Handler *svc_handler; + ACE_NEW (svc_handler, + Svc_Handler); + + // Perform a blocking connect to the server. + if (con.connect (svc_handler, + server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + else + { + // Send the data to the server. + svc_handler->send_data (); + + // Close the connection completely. + if (svc_handler->close (1) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("close"))); + } +} + +// This function runs the more sophisticated tests involving the +// Caching_Connect_Strategy. + +static void +cached_connect (STRAT_CONNECTOR &con, + const ACE_INET_Addr &server_addr) +{ + Svc_Handler *svc_handler = 0; + + for (int i = 0; i < n_client_iterations; i++) + { + // Perform a blocking connect to the server using the Strategy + // Connector with a connection caching strategy. Since we are + // connecting to the same <server_addr> these calls will return + // the same dynamically allocated <Svc_Handler> for each + // <connect>. + if (con.connect (svc_handler, + server_addr) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + return; + } + + // Send the data to the server. + svc_handler->send_data (); + + // Svc_Handler is now idle, so mark it as such and let the cache + // recycle it in another thread. + svc_handler->idle (1); + + // Rest for a second to give another thread a chance to reuse the + // connection. + ACE_OS::sleep (1); + } +} + +static void * +client_connections (void *arg) +{ + Client_Info *info = (Client_Info *) arg; + + // Run the timed-blocking test. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** starting timed-blocking connect\n"))); + timed_blocking_connect (*info->connector_, + *info->server_addr_); + +#if defined (ACE_HAS_THREADS) + // Wait for other threads to join us. + info->barrier_->wait (); +#endif /* ACE_HAS_THREADS */ + + // Run the blocking test. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** starting blocking connect\n"))); + blocking_connect (*info->connector_, + *info->server_addr_); + +#if defined (ACE_HAS_THREADS) + // Wait for other threads to join us. + info->barrier_->wait (); +#endif /* ACE_HAS_THREADS */ + + // Run the cached blocking test. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** starting cached blocking connect\n"))); + cached_connect (*info->strat_connector_, + *info->server_addr_); + return 0; +} + +// Execute the client tests. + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = reinterpret_cast<ACE_INET_Addr *> (arg); + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_DEFAULT_SERVER_HOST); + CONNECTOR connector; + + NULL_CREATION_STRATEGY creation_strategy; + NULL_ACTIVATION_STRATEGY activation_strategy; + // Configure the Strategy Connector with a strategy that caches + // connection. + CACHED_CONNECT_STRATEGY caching_connect_strategy; + + STRAT_CONNECTOR strat_connector (0, + &creation_strategy, + &caching_connect_strategy, + &activation_strategy); + Client_Info info; + info.server_addr_ = &server_addr; + info.connector_ = &connector; + info.strat_connector_ = &strat_connector; + +#if defined (ACE_HAS_THREADS) + int n_threads = n_clients; + ACE_Barrier thread_barrier (n_threads); + info.barrier_ = &thread_barrier; + + ACE_Thread_Manager client_manager; + + if (client_manager.spawn_n + (n_threads, + (ACE_THR_FUNC) client_connections, + (void *) &info, + THR_NEW_LWP) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("client thread spawn failed"), + 1)); + + // Wait for the threads to exit. + client_manager.wait (); + +#else /* ACE_HAS_THREADS */ + client_connections (&info); +#endif /* ACE_HAS_THREADS */ + return 0; +} + +// Performs the iterative server activities. + +static void * +server (void *arg) +{ +#if defined (VXWORKS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server stack size is %u\n"), + ACE_OS::thr_min_stack ())); +#endif /* VXWORKS */ + + ACCEPTOR *acceptor = (ACCEPTOR *) arg; + ACE_INET_Addr cli_addr; + ACE_TCHAR peer_host[MAXHOSTNAMELEN]; + const ACE_Time_Value tv (ACE_DEFAULT_TIMEOUT); + ACE_Synch_Options options (ACE_Synch_Options::USE_TIMEOUT, tv); + + Svc_Handler *svc_handler; + ACE_NEW_RETURN (svc_handler, + Svc_Handler, + 0); + + // Keep looping until we timeout on <accept> or fail. + + for (;;) + { + // Create a new <Svc_Handler> to consume the data. + +#if defined (ACE_LACKS_FORK) + int result = acceptor->accept (svc_handler, + &cli_addr, + options); +#else /* ! ACE_LACKS_FORK */ + int result = acceptor->accept (svc_handler, + &cli_addr); + ACE_UNUSED_ARG (options); +#endif /* ! ACE_LACKS_FORK */ + + // Timing out is the only way for threads to stop accepting + // since we don't have signals. + + if (result == -1) + { + // svc_handler->close (); The ACE_Onsehot_Acceptor closed it. + + if (errno == ETIMEDOUT) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("accept timed out\n"))); + return 0; + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("accept failed, shutting down")), + 0); + } + // Use this rather than get_host_name() to properly adjust to the + // charset width in use. + cli_addr.get_host_name (peer_host, MAXHOSTNAMELEN); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client %s connected from %d\n"), + peer_host, + cli_addr.get_port_number ())); + + svc_handler->recv_data (); + } + + ACE_NOTREACHED (return 0); +} + +#endif /* !ACE_LACKS_FORK || ACE_HAS_THREADS */ + +#if !defined (ACE_LACKS_FORK) +static void +handler (int /* signum */) +{ + // No printout here, to be safe. Signal handlers must not acquire + // locks, etc. It's not even safe to call ACE_OS::exit ()! + ACE_OS::exit (0); +} + +// Spawn threads. + +static int +spawn_processes (ACCEPTOR *acceptor, + ACE_INET_Addr *server_addr) +{ + pid_t *children_ptr = 0; + ACE_NEW_RETURN (children_ptr, + pid_t[n_servers], + -1); + ACE_Auto_Basic_Array_Ptr<pid_t> children (children_ptr); + int i; + + // Spawn off a number of server processes all of which will listen + // on the same port number for clients to connect. + for (i = 0; i < n_servers; i++) + { + pid_t pid = ACE_OS::fork (ACE_TEXT ("child")); + switch (pid) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + ACE_OS::exit (-1); + /* NOTREACHED */ + case 0: // In the child. + { + // Register a signal handler to close down the child. + ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGTERM); + ACE_UNUSED_ARG (sa); + + server ((void *) acceptor); + return 0; + /* NOTREACHED */ + } + default: // In the parent. + children[i] = pid; + break; + } + } + + client ((void *) server_addr); + + for (i = 0; i < n_servers; i++) + // Shutdown the servers. + if (ACE_OS::kill (children[i], SIGTERM) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p for %d\n"), + ACE_TEXT ("kill"), children[i])); + + pid_t child; + + do + { + child = ACE_OS::wait (); + if (child != -1) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reaping %d\n"), + child)); + } + while (child != -1); + + // Remove the lock so we don't have process semaphores lying around. + return acceptor->acceptor ().lock ().remove (); +} +#endif /* ! ACE_LACKS_FORK */ + +#if defined (ACE_LACKS_FORK) && defined (ACE_HAS_THREADS) +// Spawn threads and run the client and server. + +static +int +spawn_threads (ACCEPTOR *acceptor, + ACE_INET_Addr *server_addr) +{ + int status = 0; + +#if 0 + // Assign thread (VxWorks task) names to test that feature. + ACE_hthread_t *server_name = 0; + ACE_NEW_RETURN (server_name, + ACE_hthread_t[n_servers], + -1); + + // And test ability to provide stacks. + size_t *stack_size = 0; + ACE_NEW_RETURN (stack_size, + size_t[n_servers], + -1); + char **stack; + ACE_NEW_RETURN (stack, + char *[n_servers], + -1); + int i; + + for (i = 0; i < n_servers; ++i) + { + ACE_NEW_RETURN (server_name[i], ACE_TCHAR[32], -1); + ACE_OS::sprintf (server_name[i], + ACE_TEXT ("server%u"), + i); + stack_size[i] = 40000; + ACE_NEW_RETURN (stack[i], char[stack_size[i]], -1); + + // Initialize the stack for checkStack. + ACE_OS::memset (stack[i], 0xEE, stack_size[i]); + } + + ACE_TCHAR *client_name = ACE_TEXT ("Conn client"); +#endif /* ACE_HAS_VXTHREADS */ + + if (ACE_Thread_Manager::instance ()->spawn_n + ( + n_servers, + (ACE_THR_FUNC) server, + (void *) acceptor, + THR_NEW_LWP + , ACE_DEFAULT_THREAD_PRIORITY + , -1 + , 0 +#if 0 + , server_name +#if 0 /* Don't support setting of stack, because it doesn't seem to work. */ + , (void **) stack +#else + , 0 +#endif /* 0 */ + , stack_size +#endif /* ACE_HAS_VXTHREADS */ + ) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("server thread create failed"), + 1)); + + if (ACE_Thread_Manager::instance ()->spawn + ((ACE_THR_FUNC) client, + (void *) server_addr, + THR_NEW_LWP, + 0 +#if 0 + , &client_name +#endif /* ACE_HAS_VXTHREADS */ + ) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("client thread create failed"), + 1)); + + // Wait for the threads to exit. + // But, wait for a limited time because sometimes the test hangs on Irix. + ACE_Time_Value const max_wait (200 /* seconds */); + ACE_Time_Value const wait_time (ACE_OS::gettimeofday () + max_wait); + if (ACE_Thread_Manager::instance ()->wait (&wait_time) == -1) + { + if (errno == ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("maximum wait time of %d msec exceeded\n"), + max_wait.msec ())); + else + ACE_OS::perror (ACE_TEXT ("wait")); + + status = -1; + } + +#if 0 + for (i = 0; i < n_servers; ++i) + { + delete [] server_name[i]; + delete [] stack[i]; + } + delete [] server_name; + delete [] stack; + delete [] stack_size; +#endif /* ACE_HAS_VXTHREADS */ + + return status; +} +#endif /* ! ACE_LACKS_FORK && ACE_HAS_THREADS */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Conn_Test")); + int status = 0; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("c:i:s:")); + for (int c; (c = getopt ()) != -1; ) + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 'c': + n_clients = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'i': + n_client_iterations = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 's': + n_servers = ACE_OS::atoi (getopt.opt_arg ()); + break; + } + + // Acceptor + ACCEPTOR acceptor; + ACE_INET_Addr server_addr; + + // Bind acceptor to any port and then find out what the port was. + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1 + || acceptor.acceptor ().get_local_addr (server_addr) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("open"))); + ACE_ASSERT (0); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + if (spawn_processes (&acceptor, + &server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("spawn_processes")), + 1); +#elif defined (ACE_HAS_THREADS) + status = spawn_threads (&acceptor, &server_addr); +#else /* ACE_LACKS_FORK && ! ACE_HAS_THREADS */ + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run") + ACE_TEXT (" in a process on this platform"))); +#endif /* ACE_LACKS_FORK && ! ACE_HAS_THREADS */ + } + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/Conn_Test.h b/ACE/tests/Conn_Test.h new file mode 100644 index 00000000000..38e3cda4cd5 --- /dev/null +++ b/ACE/tests/Conn_Test.h @@ -0,0 +1,62 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Conn_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/Service_Config.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Null_Condition.h" +#include "ace/Svc_Handler.h" +#include "ace/SOCK_Stream.h" + +class Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ + // = TITLE + // This class is the product created by both <ACE_Connector> + // and <ACE_Acceptor> objects. + // + // = DESCRIPTION + // This class gets its own header file to work around AIX C++ + // compiler "features" related to template instantiation... It is + // only used by Conn_Test.cpp. +public: + Svc_Handler (ACE_Thread_Manager * = 0); + // Do-nothing constructor. + + virtual int open (void *); + // Initialization hook. + + virtual int recycle (void * = 0); + // Prepare for recycling. + + void send_data (void); + // Send data to server. + + void recv_data (void); + // Recv data from client. + + int close (u_long = 0); + // Shutdown the <Svc_Handler>. + + int idle (u_long flags); + // Set <this> to idle. +}; diff --git a/ACE/tests/DLL_Test.cpp b/ACE/tests/DLL_Test.cpp new file mode 100644 index 00000000000..46fe272f1aa --- /dev/null +++ b/ACE/tests/DLL_Test.cpp @@ -0,0 +1,173 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// DLL_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of <ACE_DLL> wrapper class. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/DLL.h" +#include "ace/Auto_Ptr.h" +#include "ace/ACE.h" +#include "ace/DLL_Manager.h" +#include "DLL_Test.h" + +ACE_RCSID(tests, DLL_Test, "$Id$") + +#if defined (ACE_WIN32) && defined (_MSC_VER) && defined (_DEBUG) +# define OBJ_SUFFIX ACE_TEXT ("d") ACE_DLL_SUFFIX +#elif defined (ACE_WIN32) && defined (__BORLANDC__) +# define OBJ_SUFFIX ACE_LD_DECORATOR_STR ACE_DLL_SUFFIX +#else +# define OBJ_SUFFIX ACE_DLL_SUFFIX +#endif /* ACE_WIN32 && && _MSC_VER && _DEBUG */ + +#if defined (ACE_WIN32) || defined (ACE_OPENVMS) +# define OBJ_PREFIX ACE_DLL_PREFIX +#else +# define OBJ_PREFIX ACE_TEXT("./") ACE_DLL_PREFIX +#endif /* ACE_WIN32 */ + +// Declare the type of the symbol: +typedef Hello *(*Hello_Factory)(void); + +typedef int ( *PFN )( Parent* ); + +int handle_test (ACE_DLL &dll) +{ + // Test the get/set_handle methods. + ACE_DLL local_dll; + + ACE_SHLIB_HANDLE handle = dll.get_handle (1); + if (handle != ACE_SHLIB_INVALID_HANDLE) + { + if (local_dll.set_handle (handle) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error setting handle.\n")), + -1); + return 0; + } + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error getting handle.\n")), + -1); +} + +int basic_test (ACE_DLL &dll) +{ + int retval = dll.open (OBJ_PREFIX + ACE_TEXT ("DLL_Test_Lib") + OBJ_SUFFIX); + + if (retval != 0) + { + ACE_TCHAR *dll_error = dll.error (); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error in DLL Open: %s\n"), + dll_error ? dll_error : ACE_TEXT ("unknown error")), + -1); + } + + // Just because the ANSI C++ spec says you can no longer cast a + // void* to a function pointer. Doesn't allow: + // TC f = (Hello_Factory) dll.symbol ("get_hello"); + void *foo; + + foo = dll.symbol (ACE_TEXT ("get_hello")); + + // Cast the void* to long first. + ptrdiff_t tmp = reinterpret_cast<ptrdiff_t> (foo); + Hello_Factory factory = reinterpret_cast<Hello_Factory> (tmp); + if (factory == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + dll.error ()), + -1); + + auto_ptr<Hello> my_hello (factory ()); + + // Make the method calls, as the object pointer is available. + my_hello->say_hello (); + my_hello->say_next (); + + // Allocate and delete a string allocated via new in a different dll. + ACE_TCHAR *new_str = my_hello->new_info (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Result for new_info(): %s\n"), new_str)); + ACE::strdelete (new_str); + + // Allocate and free a string allocated via malloc in a different dll. + ACE_TCHAR *malloc_str = my_hello->malloc_info (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Result for malloc_info(): %s\n"), malloc_str)); + ACE_OS::free (malloc_str); + + return 0; +} + +int dynamic_cast_test (ACE_DLL &dll) +{ + Child child; + child.test(); + + Parent *parent = &child; + + void * foo = dll.symbol (ACE_TEXT ("dynamic_cast_test")); + + // Cast the void* to long first. + ptrdiff_t tmp = reinterpret_cast<ptrdiff_t> (foo); + PFN pfnAcquire = reinterpret_cast<PFN> (tmp); + if (pfnAcquire == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + dll.error ()), + -1); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("before %@ %@\n"), + &child, dynamic_cast<Child*> (parent))); + + if (pfnAcquire (&child) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("dynamic_cast failed.\n")), -1); + + return 0; +} + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("DLL_Test")); + + int retval = 0; + +// Protection against this test being run on platforms not supporting Dlls. +#if defined(ACE_HAS_DYNAMIC_LINKING) + + ACE_DLL dll; + + retval += basic_test (dll); + + retval += dynamic_cast_test (dll); + + retval += handle_test (dll); + + // Call close here so that any errors make it into the log. + dll.close (); + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Dynamically Linkable Libraries not supported on this platform\n"))); +#endif /* ACE_HAS_DYNAMIC_LINKING */ + + ACE_END_TEST; + return retval == 0 ? 0 : 1; +} + diff --git a/ACE/tests/DLL_Test.h b/ACE/tests/DLL_Test.h new file mode 100644 index 00000000000..fb55875eff9 --- /dev/null +++ b/ACE/tests/DLL_Test.h @@ -0,0 +1,74 @@ +// -*- C++ -*- + +// ================================================================ +/** + * @file DLL_Test.h + * + * $Id$ + * + * @author Kirthika Parameswaran <kirthika@cs.wustl.edu> + */ +// ================================================================ + + +#ifndef ACE_TESTS_DLL_TEST_H +#define ACE_TESTS_DLL_TEST_H + +#include "DLL_Test_Parent.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Log_Msg.h" +#include "ace/Trace.h" +#include "ace/svc_export.h" + +/** + * @class Hello + * + * @brief Define the interface used by the DLL_Test + */ +class Hello +{ +public: + /// Destructor + virtual ~Hello (void) + { + ACE_TRACE ("Hello::~Hello"); + } + + /** + * @name Methods invoked by the test + * + * The test invokes four methods, a non-virtual method and a three virtual + * methods implemented in the shared library. + */ + //@{ + void say_hello (void) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Hello\n"))); + } + + virtual void say_next (void) = 0; + + virtual ACE_TCHAR *new_info (void) = 0; + + virtual ACE_TCHAR *malloc_info (void) = 0; + //@} +}; + + +// Used to test dynamic_cast<> in shared libraries. +class ACE_Svc_Export Child : public Parent +{ +public: + + virtual ~Child (void); + + virtual void test (void); + +}; + +#endif /* ACE_TESTS_DLL_TEST_H */ diff --git a/ACE/tests/DLL_Test_Impl.cpp b/ACE/tests/DLL_Test_Impl.cpp new file mode 100644 index 00000000000..3b0f4ff6149 --- /dev/null +++ b/ACE/tests/DLL_Test_Impl.cpp @@ -0,0 +1,145 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// DLL_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of <ACE_DLL> wrapper class. +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#include "DLL_Test_Impl.h" +#include "ace/ACE.h" +#include "ace/OS_Errno.h" +#include "ace/svc_export.h" +#include "ace/OS_NS_string.h" + +ACE_RCSID (tests, + DLL_Test_Impl, + "$Id$") + +Hello_Impl::Hello_Impl (void) +{ + ACE_DEBUG ((LM_DEBUG, "Hello_Impl::Hello_Impl\n")); +} + +Hello_Impl::~Hello_Impl (void) +{ + ACE_DEBUG ((LM_DEBUG, "Hello_Impl::~Hello_Impl\n")); +} + +void +Hello_Impl::say_next (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("How are you?\n"))); +} + +ACE_TCHAR * +Hello_Impl::new_info (void) +{ + return ACE::strnew (ACE_TEXT ("Hello_Impl::new_info() allocated by ACE::strnew()")); +} + +ACE_TCHAR * +Hello_Impl::malloc_info (void) +{ + return ACE_OS::strdup (ACE_TEXT ("Hello_Impl::new_info() allocated by ACE_OS::malloc()")); +} + +void * +Hello_Impl::operator new (size_t bytes) +{ + ACE_DEBUG ((LM_INFO, "Hello_Impl::new\n")); + return ::new char[bytes]; +} + +#if defined (ACE_HAS_NEW_NOTHROW) + /// Overloaded new operator, nothrow_t variant. +void * +Hello_Impl::operator new (size_t bytes, const ACE_nothrow_t &nt) +{ + ACE_DEBUG ((LM_INFO, "Hello_Impl::new\n")); + return ::new (nt) char[bytes]; +} + +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) +void +Hello_Impl::operator delete (void *ptr, const ACE_nothrow_t&) throw () +{ + ACE_DEBUG ((LM_INFO, "Hello_Impl::delete\n")); + ::delete [] static_cast<char *> (ptr); +} +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ + +#endif /* ACE_HAS_NEW_NOTHROW */ + +void +Hello_Impl::operator delete (void *ptr) +{ + ACE_DEBUG ((LM_INFO, "Hello_Impl::delete\n")); + ::delete [] static_cast<char *> (ptr); +} + +extern "C" ACE_Svc_Export Hello * +get_hello (void) +{ + Hello *hello = 0; + + ACE_NEW_RETURN (hello, + Hello_Impl, + 0); + + return hello; +} + +class Static_Constructor_Test +{ +public: + Static_Constructor_Test (void) + { + ACE_DEBUG ((LM_DEBUG, + "Static_Constructor_Test::Static_Constructor_Test\n")); + } + ~Static_Constructor_Test (void) + { + ACE_DEBUG ((LM_DEBUG, + "Static_Constructor_Test::~Static_Constructor_Test\n")); + } +}; + +static Static_Constructor_Test the_instance; + +// -------------------------------------------------------- + +Child::~Child (void) +{ +} + +void +Child::test (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("child called\n"))); +} + +// -------------------------------------------------------- + + +// Test dynamic cast +extern "C" ACE_Svc_Export int +dynamic_cast_test (Parent *target) +{ + Child *c = 0; + c = dynamic_cast<Child*> (target); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("dynamic_cast_test: parent %@; child %@\n"), + target, c)); + return target == c ? 0 : -1; +} diff --git a/ACE/tests/DLL_Test_Impl.h b/ACE/tests/DLL_Test_Impl.h new file mode 100644 index 00000000000..9ef3fb887a8 --- /dev/null +++ b/ACE/tests/DLL_Test_Impl.h @@ -0,0 +1,68 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// DLL_Test_Impl.h +// +// = AUTHOR +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_DLL_TEST_IMPL_H +#define ACE_TESTS_DLL_TEST_IMPL_H + +#include "DLL_Test.h" +#include "ace/OS_Memory.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +class Hello_Impl : public Hello +{ + // = TITLE + // The Hello class in the dynamically linkable library. + // + // = DESCRIPTION + // This class is used in this example to show how a library can + // be loaded on demand and its methods called on getting the + // symbols from the library. +public: + Hello_Impl (void); + // Constructor + + ~Hello_Impl (void); + // Destructor + + void say_next (void); + // See the documentation in the base class + + ACE_TCHAR *new_info (void); + // Uses ACE::strnew() to allocate the returned string. + + ACE_TCHAR *malloc_info (void); + // Uses ACE_OS::malloc() to allocate the returned string. + + // Overload the new/delete opertors so the object will be + // created/deleted using the memory allocator associated with the + // DLL/SO. + void *operator new (size_t bytes); + +#if defined (ACE_HAS_NEW_NOTHROW) + /// Overloaded new operator, nothrow_t variant. + void *operator new (size_t bytes, const ACE_nothrow_t &nt); +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) + void operator delete (void *p, const ACE_nothrow_t&) throw (); +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ +#endif /* ACE_HAS_NEW_NOTHROW */ + + void operator delete (void *ptr); + +}; + +#endif /* ACE_TESTS_DLL_TEST_IMPL_H */ diff --git a/ACE/tests/DLL_Test_Parent.cpp b/ACE/tests/DLL_Test_Parent.cpp new file mode 100644 index 00000000000..0664f5a7c92 --- /dev/null +++ b/ACE/tests/DLL_Test_Parent.cpp @@ -0,0 +1,19 @@ +// $Id$ + +#include "DLL_Test_Parent.h" +#include "ace/Log_Msg.h" + +ACE_RCSID (tests, + DLL_Test_Parent, + "$Id$") + + +Parent::~Parent (void) +{ +} + +void +Parent::test (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("parent called\n"))); +} diff --git a/ACE/tests/DLL_Test_Parent.h b/ACE/tests/DLL_Test_Parent.h new file mode 100644 index 00000000000..3046dad48d4 --- /dev/null +++ b/ACE/tests/DLL_Test_Parent.h @@ -0,0 +1,34 @@ +// -*- C++ -*- + +// ================================================================ +/** + * @file DLL_Test_Parent.h + * + * $Id$ + * + * @author Kirthika Parameswaran <kirthika@cs.wustl.edu> + * @author Ossama Othman <ossama@dre.vanderbilt.edu> + */ +// ================================================================ + +#ifndef ACE_TESTS_DLL_TEST_PARENT_H +#define ACE_TESTS_DLL_TEST_PARENT_H + +#include "DLL_Test_Parent_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + + +class DLL_Test_Parent_Export Parent +{ +public: + + virtual ~Parent (void); + + virtual void test (void); + +}; + +#endif /* ACE_TESTS_DLL_TEST_PARENT_H */ diff --git a/ACE/tests/DLL_Test_Parent_Export.h b/ACE/tests/DLL_Test_Parent_Export.h new file mode 100644 index 00000000000..226d40f5097 --- /dev/null +++ b/ACE/tests/DLL_Test_Parent_Export.h @@ -0,0 +1,58 @@ + +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. +// This file is generated automatically by generate_export_file.pl -s DLL_Test_Parent +// ------------------------------ +#ifndef DLL_TEST_PARENT_EXPORT_H +#define DLL_TEST_PARENT_EXPORT_H + +#include "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (DLL_TEST_PARENT_HAS_DLL) +# define DLL_TEST_PARENT_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && DLL_TEST_PARENT_HAS_DLL */ + +#if !defined (DLL_TEST_PARENT_HAS_DLL) +# define DLL_TEST_PARENT_HAS_DLL 1 +#endif /* ! DLL_TEST_PARENT_HAS_DLL */ + +#if defined (DLL_TEST_PARENT_HAS_DLL) && (DLL_TEST_PARENT_HAS_DLL == 1) +# if defined (DLL_TEST_PARENT_BUILD_DLL) +# define DLL_Test_Parent_Export ACE_Proper_Export_Flag +# define DLL_TEST_PARENT_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# define DLL_TEST_PARENT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_EXPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# else /* DLL_TEST_PARENT_BUILD_DLL */ +# define DLL_Test_Parent_Export ACE_Proper_Import_Flag +# define DLL_TEST_PARENT_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# define DLL_TEST_PARENT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_IMPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# endif /* DLL_TEST_PARENT_BUILD_DLL */ +#else /* DLL_TEST_PARENT_HAS_DLL == 1 */ +# define DLL_Test_Parent_Export +# define DLL_TEST_PARENT_SINGLETON_DECLARATION(T) +# define DLL_TEST_PARENT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +#endif /* DLL_TEST_PARENT_HAS_DLL == 1 */ + +// Set DLL_TEST_PARENT_NTRACE = 0 to turn on library specific tracing even if +// tracing is turned off for ACE. +#if !defined (DLL_TEST_PARENT_NTRACE) +# if (ACE_NTRACE == 1) +# define DLL_TEST_PARENT_NTRACE 1 +# else /* (ACE_NTRACE == 1) */ +# define DLL_TEST_PARENT_NTRACE 0 +# endif /* (ACE_NTRACE == 1) */ +#endif /* !DLL_TEST_PARENT_NTRACE */ + +#if (DLL_TEST_PARENT_NTRACE == 1) +# define DLL_TEST_PARENT_TRACE(X) +#else /* (DLL_TEST_PARENT_NTRACE == 1) */ +# if !defined (ACE_HAS_TRACE) +# define ACE_HAS_TRACE +# endif /* ACE_HAS_TRACE */ +# define DLL_TEST_PARENT_TRACE(X) ACE_TRACE_IMPL(X) +# include "ace/Trace.h" +#endif /* (DLL_TEST_PARENT_NTRACE == 1) */ + +#endif /* DLL_TEST_PARENT_EXPORT_H */ + +// End of auto generated file. diff --git a/ACE/tests/DLList_Test.cpp b/ACE/tests/DLList_Test.cpp new file mode 100644 index 00000000000..57155d177ab --- /dev/null +++ b/ACE/tests/DLList_Test.cpp @@ -0,0 +1,129 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// DLList_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of <ACE_DLList>. +// +// = AUTHOR +// James Hu <jxh@cs.wustl.edu> and Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Containers.h" +#include "ace/SString.h" +#include "ace/Malloc_T.h" + +typedef ACE_TCHAR *ACE_STRING; +typedef ACE_DLList<ACE_STRING> STRLIST; +typedef ACE_DLList_Iterator<ACE_STRING> STRLIST_ITERATOR; +typedef ACE_DLList_Reverse_Iterator<ACE_STRING> STRLIST_REVERSE_ITERATOR; + +static ACE_STRING string_table[] = +{ + // Note: all these casts are to appease SC 5.0 which is not pleased + // with using string literals (i.e. const char *'s) as char + // *'s. It's ugly, but necessary. + const_cast<ACE_TCHAR *> (ACE_TEXT ("hello")), + const_cast<ACE_TCHAR *> (ACE_TEXT ("guten Tag")), + const_cast<ACE_TCHAR *> (ACE_TEXT ("goodbye")), + const_cast<ACE_TCHAR *> (ACE_TEXT ("auf wiedersehen")), + const_cast<ACE_TCHAR *> (ACE_TEXT ("funny")), + const_cast<ACE_TCHAR *> (ACE_TEXT ("lustig")), + 0 +}; + +static void +run_iterate (STRLIST &list) +{ + ACE_STRING *entry; + size_t i = 0; + + for (STRLIST_ITERATOR iter (list); + (entry = iter.next ()) != 0; + iter.advance (), i++) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("iterating (%d): [%s]\n"), + i, + (ACE_TCHAR *) *entry)); + } +} + +static void +run_reverse_iterate (STRLIST &list) +{ + ACE_STRING *entry; + size_t i = 0; + + for (STRLIST_REVERSE_ITERATOR iter (list); + iter.next (entry) != 0; + iter.advance (), i++) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("iterating (%d): [%s]\n"), + i, + (ACE_TCHAR *) *entry)); + } +} + +static int +run_test (void) +{ + STRLIST list; + + size_t i; + + for (i = 0; string_table[i] != 0; i++) + { + if (ACE_EVEN (i) + && list.insert_tail ((ACE_STRING *) &string_table[i]) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p failed for %s \n"), + ACE_TEXT ("insert"), + string_table[i]), + -1); + else if (list.insert_head ((ACE_STRING *) &string_table[i]) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p failed for %s \n"), + ACE_TEXT ("insert"), + string_table[i]), + -1); + run_iterate (list); + run_reverse_iterate (list); + } + + run_iterate (list); + run_reverse_iterate (list); + + list.delete_tail (); + list.delete_tail (); + + run_iterate (list); + run_reverse_iterate (list); + + list.delete_head (); + list.delete_head (); + + run_iterate (list); + run_reverse_iterate (list); + + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("DLList_Test")); + + run_test (); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Date_Time_Test.cpp b/ACE/tests/Date_Time_Test.cpp new file mode 100644 index 00000000000..56c1fc230e6 --- /dev/null +++ b/ACE/tests/Date_Time_Test.cpp @@ -0,0 +1,132 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program verifies the ACE_Date_Time class. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "ace/Date_Time.h" +#include "test_config.h" + +ACE_RCSID(tests, Date_Time_Test, "$Id$") + +static ACE_Date_Time static_dt; // Making sure it doesn't crash. + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Date_Time_Test")); + + int error = 0; + + ACE_Date_Time dt; + + long month = dt.month (); + long day = dt.day (); + long year = dt.year (); + long hour = dt.hour (); + long minute = dt.minute (); + long seconds = dt.second (); + long usec = dt.microsec (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_Date_Time (m/d/y, h:m:s.u): %d/%d/%d, %d:%d:%d.%d\n"), + month, day, year, hour, minute, seconds, usec)); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE_Log thinks it is: %D\n"))); + + if (month < 1 || month > 12) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Month (%d) out of range (1-12)\n"), + month)); + error = 1; + } + if (day < 1 || day > 31) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Day (%d) out of range (1-31)\n"), + day)); + error = 1; + } + if (year < 1900 || year > 2100) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Year (%d) out of range (1900-2100)\n"), + year)); + error = 1; + } + if (hour < 0 || hour > 23) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Hour (%d) out of range (0-23)\n"), + hour)); + error = 1; + } + if (minute < 0 || minute > 59) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Minute (%d) out of range (0-59)\n"), + minute)); + error = 1; + } + if (seconds < 0 || seconds > 59) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Seconds (%d) out of range (0-59)\n"), + seconds)); + error = 1; + } + if (usec < 0 || usec > 999999) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Microseconds (%d) out of range (0-999999)\n"), + usec)); + error = 1; + } + + // The static ACE_Date_Time object is primarily to be sure it doesn't + // crash; However, let's do some sanity checks on it to be sure it's + // legit as well. + if (static_dt.month () != month) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Static month (%d) doesn't match %d\n"), + static_dt.month (), month)); + error = 1; + } + if (static_dt.day () != day) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Static day (%d) doesn't match %d\n"), + static_dt.day (), day)); + error = 1; + } + if (static_dt.year () != year) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Static year (%d) doesn't match %d\n"), + static_dt.year (), year)); + error = 1; + } + if (static_dt.hour () != hour) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Static hour (%d) doesn't match %d\n"), + static_dt.hour (), hour)); + error = 1; + } + + // There's a rare instance where the starting seconds is 59 and the + // minute roles over during the test run. + if (!(static_dt.minute () == minute || + (static_dt.minute () + 1 == minute && static_dt.second () > seconds))) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Static minute (%d) doesn't match %d\n"), + static_dt.minute (), minute)); + error = 1; + } + ACE_END_TEST; + return error; +} diff --git a/ACE/tests/Dev_Poll_Reactor_Test.cpp b/ACE/tests/Dev_Poll_Reactor_Test.cpp new file mode 100644 index 00000000000..81dbd1f49f4 --- /dev/null +++ b/ACE/tests/Dev_Poll_Reactor_Test.cpp @@ -0,0 +1,598 @@ +//============================================================================= +/** + * @file Dev_Poll_Reactor_Test.cpp + * + * $Id$ + * + * This test verifies that the Dev_Poll_Reactor is functioning + * properly, and demonstrates how "speculative reads" can be + * performed. "Speculative reads" are necessary when using event + * demultiplexing mechanisms that use a "state change" interface. + * Similarly, "speculative writes" may be necessary, i.e. keep + * writing until the connection is flow controlled. An example of a + * demuxing mechanism with such an interface is Linux's `/dev/epoll' + * character device. Mechansims with state change interfaces are + * also said to be "edge triggered," versus "level triggered" + * mechanisms such as select(). + * + * @author Ossama Othman <ossama@dre.vanderbilt.edu> + */ +//============================================================================= + +#include "test_config.h" + +ACE_RCSID (tests, + Dev_Poll_Reactor_Test, + "$Id$") + + +#if defined (ACE_HAS_DEV_POLL) || defined (ACE_HAS_EVENT_POLL) + +#include "ace/OS_NS_signal.h" +#include "ace/Reactor.h" +#include "ace/Dev_Poll_Reactor.h" + +#include "ace/Acceptor.h" +#include "ace/Connector.h" + +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Stream.h" + +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_netdb.h" + + +typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> SVC_HANDLER; + +// ---------------------------------------------------- + +class Client : public SVC_HANDLER +{ +public: + + Client (void); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0); + //FUZZ: enable check_for_lack_ACE_OS + + virtual int handle_output (ACE_HANDLE handle); + + virtual int handle_timeout (const ACE_Time_Value ¤t_time, + const void *act); + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask mask); + +private: + + unsigned int call_count_; + +}; + + +class Server : public SVC_HANDLER +{ +public: + + Server (void); + + virtual int handle_input (ACE_HANDLE handle); + + virtual int handle_timeout (const ACE_Time_Value ¤t_time, + const void *act); + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask mask); + +private: + + unsigned int call_count_; + +}; + +// ---------------------------------------------------- + +Client::Client (void) + : call_count_ (0) +{ +} + +int +Client::open (void *) +{ + // ACE_ASSERT (this->reactor () != 0); + + if (this->reactor () + && this->reactor ()->register_handler ( + this, + ACE_Event_Handler::WRITE_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("unable to register client handler")), + -1); + + return 0; +} + +int +Client::handle_output (ACE_HANDLE) +{ + for (int i = 1; i <= 5; ++i) + { + char buffer[BUFSIZ] = { 0 }; + + ACE_OS::sprintf (buffer, "test message %d.\n", i); + + ssize_t bytes_sent = + this->peer ().send (buffer, ACE_OS::strlen (buffer)); + + if (bytes_sent == -1) + { + if (errno == EWOULDBLOCK) + return 0; // Flow control kicked in. + else if (errno == EPIPE || errno == ECONNRESET) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client::handle_output; server ") + ACE_TEXT ("closed handle %d\n"), + this->peer ().get_handle ())); + return -1; + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::handle_output")), + -1); + } + else if (bytes_sent == 0) + return -1; + else + ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%t) Sent %s"), buffer)); + } + + return 0; +} + +int +Client::handle_timeout (const ACE_Time_Value &, const void *) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Expected client timeout occured at: %T\n"))); + + this->call_count_++; + + int status = this->handle_output (this->get_handle ()); + if (status == -1 || this->call_count_ > 10) + { + if (this->reactor ()->end_reactor_event_loop () == 0) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Successful client reactor shutdown.\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Failed client reactor shutdown"))); + + // Force this service handler to be closed in either case. + return -1; + } + + return 0; +} + +int +Client::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask mask) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Client Svc_Handler closed ") + ACE_TEXT ("handle <%d> with reactor mask <0x%x>.\n"), + handle, + mask)); + + return 0; +} + +// ---------------------------------------------------- + +Server::Server (void) + : call_count_ (0) +{ +} + +int +Server::handle_input (ACE_HANDLE /* handle */) +{ + char buffer[BUFSIZ+1] = { 0 }; // Insure a trailing nul + ssize_t bytes_read = 0; + + char * const begin = buffer; + char * const end = buffer + BUFSIZ; + + for (char * buf = begin; buf < end; buf += bytes_read) + { + // Keep reading until it is no longer possible to do so. + // + // This is done since the underlying event demultiplexing + // mechanism may have a "state change" interface (as opposed to + // "state monitoring"), in which case a "speculative" read is + // done. + bytes_read = this->peer ().recv (buf, end - buf); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("****** bytes_read = %d\n"), + bytes_read)); + + if (bytes_read == -1) + { + if (errno == EWOULDBLOCK) + { + +// ACE_HEX_DUMP ((LM_DEBUG, +// buf, +// 80, +// "BUFFER CONTENTS")); + if (buf == buffer) + return 0; + else + break; + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Server::handle_input")), + -1); + } + else if (bytes_read == 0) + return -1; + } + + ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%t) Message received: %s\n"), buffer)); + + return 0; +} + +int +Server::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Expected server timeout occured at: %T\n"))); + +// if (this->call_count_ == 0 +// && this->handle_input (this->get_handle ()) != 0 +// && errno != EWOULDBLOCK) +// return -1; + +// ACE_DEBUG ((LM_INFO, +// "SERVER HANDLE = %d\n", +// this->get_handle ())); + + + this->call_count_++; + + if (this->call_count_ > 10) + { + if (this->reactor ()->end_reactor_event_loop () == 0) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Successful server reactor shutdown.\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Failed server reactor shutdown"))); + + // Force this service handler to be closed in either case. + return -1; + } + + return 0; +} + +int +Server::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask mask) +{ + if (this->call_count_ > 4) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Server Svc_Handler closing ") + ACE_TEXT ("handle <%d,%d> with reactor mask <0x%x>.\n"), + handle, + this->get_handle (), + mask)); + + return this->peer ().close (); + } + + return 0; +} + +// ---------------------------------------------------- + +typedef ACE_Acceptor<Server, ACE_SOCK_ACCEPTOR> ACCEPTOR; +typedef ACE_Connector<Client, ACE_SOCK_CONNECTOR> CONNECTOR; + +// ---------------------------------------------------- + +class TestAcceptor : public ACCEPTOR +{ +public: + + virtual int accept_svc_handler (Server * handler) + { + int result = this->ACCEPTOR::accept_svc_handler (handler); + + if (result != 0) + { + if (errno != EWOULDBLOCK) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to accept connection"))); + + return result; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Accepted connection. ") + ACE_TEXT ("Stream handle: <%d>\n"), + handler->get_handle ())); + +// if (handler->handle_input (handler->get_handle ()) == -1 +// && errno != EWOULDBLOCK) +// return -1; + +// #if 0 + ACE_Time_Value delay (2, 0); + ACE_Time_Value restart (2, 0); + if (handler->reactor ()->schedule_timer (handler, + 0, + delay, + restart) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to schedule server side ") + ACE_TEXT ("timer in ACE_Dev_Poll_Reactor")), + -1); + } +// #endif /* 0 */ + + return result; + } + +}; + +// ---------------------------------------------------- + +class TestConnector : public CONNECTOR +{ +public: + + virtual int connect_svc_handler ( + CONNECTOR::handler_type *& handler, + const CONNECTOR::addr_type &remote_addr, + ACE_Time_Value *timeout, + const CONNECTOR::addr_type &local_addr, + int reuse_addr, + int flags, + int perms) + { + const int result = this->CONNECTOR::connect_svc_handler (handler, + remote_addr, + timeout, + local_addr, + reuse_addr, + flags, + perms); + + if (result != 0) + return result; + + ACE_TCHAR hostname[MAXHOSTNAMELEN]; + if (remote_addr.get_host_name (hostname, + sizeof (hostname)) != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to retrieve hostname")), + -1); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Connected to <%s:%d>.\n"), + hostname, + (int) remote_addr.get_port_number ())); + +// #if 0 + ACE_Time_Value delay (4, 0); + ACE_Time_Value restart (3, 0); + if (handler->reactor ()->schedule_timer (handler, + 0, + delay, + restart) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to schedule client side ") + ACE_TEXT ("timer in ACE_Dev_Poll_Reactor")), + -1); + } +// #endif /* 0 */ + + return result; + } + + virtual int connect_svc_handler ( + CONNECTOR::handler_type *& handler, + CONNECTOR::handler_type *& sh_copy, + const CONNECTOR::addr_type &remote_addr, + ACE_Time_Value *timeout, + const CONNECTOR::addr_type &local_addr, + int reuse_addr, + int flags, + int perms) { + sh_copy = handler; + return this->connect_svc_handler (handler, remote_addr, timeout, + local_addr, reuse_addr, flags, + perms); + } +}; + +// ---------------------------------------------------- + +ACE_THR_FUNC_RETURN +server_worker (void *p) +{ + const unsigned short port = *(static_cast<unsigned short *> (p)); + + ACE_INET_Addr addr; + + if (addr.set (port, INADDR_LOOPBACK) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("server_worker - ACE_INET_Addr::set"))); + + return (void *) -1; + } + + ACE_Dev_Poll_Reactor dp_reactor; + dp_reactor.restart (1); // Restart on EINTR + ACE_Reactor reactor (&dp_reactor); + + TestAcceptor server; + + int flags = 0; + ACE_SET_BITS (flags, ACE_NONBLOCK); // Enable non-blocking in the + // Svc_Handlers. + + if (server.open (addr, &reactor, flags) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to open server service handler"))); + + return (void *) -1; + } + + if (reactor.run_reactor_event_loop () != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Error when running server ") + ACE_TEXT ("reactor event loop"))); + + return (void *) -1; + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) Reactor event loop finished ") + ACE_TEXT ("successfully.\n"))); + + return 0; +} + +// ---------------------------------------------------- + +// struct server_arg +// { +// unsigned short port; + +// ACE_Condition<ACE_SYNCH_MUTEX> * cv; +// }; + +// ---------------------------------------------------- + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Dev_Poll_Reactor_Test")); + + // Make sure we ignore SIGPIPE + sigset_t sigsetNew[1]; + sigset_t sigsetOld[1]; + ACE_OS::sigemptyset (sigsetNew); + ACE_OS::sigaddset (sigsetNew, SIGPIPE); + ACE_OS::sigprocmask (SIG_BLOCK, sigsetNew, sigsetOld); + + ACE_Dev_Poll_Reactor dp_reactor; + dp_reactor.restart (1); // Restart on EINTR + ACE_Reactor reactor (&dp_reactor); + + TestConnector client; + + int flags = 0; + ACE_SET_BITS (flags, ACE_NONBLOCK); // Enable non-blocking in the + // Svc_Handlers. + + if (client.open (&reactor, flags) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to open client service handler")), + -1); + +// ACE_SYNCH_MUTEX mutex; +// ACE_Condition<ACE_SYNCH_MUTEX> cv (mutex); + +// server_arg arg; +// arg.port = 54678; // Port the server will listen on. +// arg.cv = &cv; + + unsigned short port = 54678; + + if (ACE_Thread_Manager::instance ()->spawn (server_worker, &port) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to spawn server thread")), + -1); + + ACE_OS::sleep (5); // Wait for the listening endpoint to be set up. + + ACE_INET_Addr addr; + if (addr.set (port, INADDR_LOOPBACK) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_INET_Addr::set")), + -1); + + Client *client_handler = 0; + + if (client.connect (client_handler, addr) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Unable to connect to server")), + -1); + + if (reactor.run_reactor_event_loop () != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Error when running client ") + ACE_TEXT ("reactor event loop")), + -1); + + if (ACE_Thread_Manager::instance ()->wait () != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Error waiting for threads to complete")), + -1); + + ACE_END_TEST; + + return 0; +} + +#else + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Dev_Poll_Reactor_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Dev Poll and Event Poll are not supported ") + ACE_TEXT ("on this platform\n"))); + ACE_END_TEST; + return 0; +} + +#endif /* ACE_HAS_DEV_POLL || ACE_HAS_EVENT_POLL */ diff --git a/ACE/tests/Dirent_Test.cpp b/ACE/tests/Dirent_Test.cpp new file mode 100644 index 00000000000..fcff9040409 --- /dev/null +++ b/ACE/tests/Dirent_Test.cpp @@ -0,0 +1,403 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Dirent_Test.cpp +// +// = DESCRIPTION +// This is a test of the opendir and readdir emulation provided by the +// class ACE_Dirent. It is used to ensure that the emulation code +// works properly on platforms that don't support this capability +// natively. As the emulation code is not compiled in other +// platforms, this test also ensures that there is no impact to +// platforms that natively support directory scanning operations. +// +// = AUTHOR +// Phil Mesnier <mesnier_p@ociweb.com>, +// Zvika Ashani <zvika@aspectusvi.com>, +// Rich Newman <RNewman@directv.com>, and +// Douglas C. Schmidt <d.schmidt@vanderbilt.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Dirent.h" +#include "ace/Dirent_Selector.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_String.h" +#include "ace/SString.h" + + +ACE_RCSID (tests, + Dirent_Test, + "$Id Dirent_Test.cpp,v 4.10 2003/05/18 19:17:34 dhinton Exp$") + + +#if (defined (ACE_VXWORKS) && (ACE_VXWORKS < 0x600)) +# define TEST_DIR "log" +# define DIR_DOT "." +# define DIR_DOT_DOT ".." +# define TEST_ENTRY ".." +#else +# if defined (ACE_LACKS_STRUCT_DIR) || !defined (ACE_HAS_SCANDIR) +# define DIR_DOT ACE_TEXT (".") +# define DIR_DOT_DOT ACE_TEXT ("..") +# define TEST_ENTRY ACE_TEXT ("run_test.lst") +# else +# define DIR_DOT "." +# define DIR_DOT_DOT ".." +# define TEST_ENTRY "run_test.lst" +# endif /* ACE_LACKS_STRUCT_DIR */ +#endif /* ACE_VXWORKS < 0x600 */ + +// Directory to scan - we need to figure it out based on environment. +static ACE_TString TestDir; +static const int RECURSION_INDENT = 3; + +// Number of entries in the directory. +static int entrycount = 0; + +extern "C" { + +static int +selector (const ACE_DIRENT *d) +{ + return ACE_OS::strcmp (d->d_name, TEST_ENTRY) == 0; +} + +static int +comparator (const ACE_DIRENT **d1, const ACE_DIRENT **d2) +{ + return ACE_OS::alphasort (d1, d2); +} + +} /* extern "C" */ + +static int +dirent_selector_test (void) +{ + int status; + int n; + int error = 0; + const ACE_TCHAR *test_dir = TestDir.c_str (); + ACE_Dirent_Selector sds; + + // Pass in functions that'll specify the selection criteria. + status = sds.open (test_dir, selector, comparator); + if (status == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s, %p\n"), + test_dir, + ACE_TEXT ("open")), + -1); + + // We should only have located ourselves! + if (sds.length () != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("selected %d entries in %s, should be 1\n"), + sds.length (), + test_dir)); + error = 1; + } + + for (n = 0; n < sds.length (); ++n) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sorted: %d: %C\n"), + n, + sds[n]->d_name)); + + status = sds.close (); + if (status == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("after selecting, %p\n"), + ACE_TEXT ("close"))); + error = 1; + } + + ACE_Dirent_Selector ds; + + // Don't specify any selection criteria. + status = ds.open (test_dir); + if (status == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s w/o selection criteria; %p\n"), + test_dir, + ACE_TEXT ("open"))); + error = 1; + } + + // We counted the entries earlier by hand; should be the same here. + if (entrycount != ds.length ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Counted %d entries in %s but selector says %d\n"), + entrycount, + test_dir, + ds.length ())); + error = 1; + } + + for (n = 0; n < ds.length (); ++n) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Entry %d: %C\n"), + n, + ds[n]->d_name)); + + if (ds.close () == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("w/o selection criteria; %p\n"), + ACE_TEXT ("close"))); + error = 1; + } + + return error ? -1 : 0; +} + +static int +dirent_test (void) +{ + ACE_Dirent dir (TestDir.c_str ()); + + for (ACE_DIRENT *directory; + (directory = dir.read ()) != 0; + entrycount++) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Entry %d: %C\n"), + entrycount, + directory->d_name)); + switch (entrycount) + { + case 0: + ACE_ERROR_RETURN + ((LM_ERROR, ACE_TEXT ("readdir failed to read anything\n")), -1); + /* NOTREACHED */ + case 1: + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("readdir failed, only matched directory name\n")), + -1); + /* NOTREACHED */ + default: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("readdir succeeded, read %d entries\n"), + entrycount)); + } + return 0; +} + +static int +dirent_count (const ACE_TCHAR *dir_path, + int &dir_count, + int &file_count, + int recursion_level) +{ +#if !defined (ACE_LACKS_CHDIR) + +# if (defined (ACE_VXWORKS) && (ACE_VXWORKS < 0x600)) + // VxWorks only allows full paths (incl. device spec if applicable) to be specified + ACE_TCHAR full_path[MAXPATHLEN]; + if (ACE_OS::getcwd (full_path, sizeof(full_path)) == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("getcwd: failed\n")), + -1); + if ((ACE_OS::strlen (full_path) + 1 + ACE_OS::strlen (dir_path)) >= sizeof(full_path)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("getcwd: too long\n")), + -1); + ACE_OS::strcat (ACE_OS::strcat (full_path, "/"), dir_path); + if (ACE_OS::chdir (full_path) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("chdir: %p\n"), + full_path), + -1); +# else + if (ACE_OS::chdir (dir_path) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("chdir: %p\n"), + dir_path), + -1); +# endif +#endif /* !ACE_LACKS_CHDIR */ + + ACE_Dirent dir (ACE_TEXT (".")); + + // Since the dir struct d_name type changes depending on the setting + // of ACE_LACKS_STRUCT_DIR, copy each name into a neutral format + // array to work on it. + const size_t maxnamlen = MAXNAMLEN; + ACE_TCHAR tname[maxnamlen + 1]; + + int entry_count = 0; + + for (ACE_DIRENT *directory; (directory = dir.read ()) != 0;) + { + // Skip the ".." and "." files. + if (ACE_OS::strcmp (directory->d_name, DIR_DOT) == 0 + || ACE_OS::strcmp (directory->d_name, DIR_DOT_DOT) == 0) + continue; + ++entry_count; + +#if !defined (ACE_LACKS_STRUCT_DIR) && !defined (__BORLANDC__) + ACE_OS::strncpy (tname, + ACE_TEXT_CHAR_TO_TCHAR (directory->d_name), + maxnamlen); +#else + ACE_OS::strncpy (tname, directory->d_name, maxnamlen); +#endif /* ACE_LACKS_STRUCT_DIR */ + + int local_file_count = 0; + int local_dir_count = 0; + ACE_stat stat_buf; + if (ACE_OS::lstat (directory->d_name, &stat_buf) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + tname), + -1); + + switch (stat_buf.st_mode & S_IFMT) + { + case S_IFREG: // Either a regular file or an executable. + ++file_count; + break; + + case S_IFLNK: // Either a file or directory link, so let's find out. + if (ACE_OS::stat (directory->d_name, &stat_buf) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + tname), + -1); + + if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) + // Don't recurse through symbolic directory links! + ++dir_count; + else + ++file_count; + break; + + case S_IFDIR: + ACE_DEBUG ((LM_DEBUG, "%*sentering subdirectory %s\n", + recursion_level * RECURSION_INDENT, + ACE_TEXT (""), + tname)); + if (dirent_count (tname, + local_dir_count, + local_file_count, + recursion_level + 1) != -1) + { + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("%*ssubdirectory %s has %d files and %d subdirectories.\n"), + recursion_level * RECURSION_INDENT, + ACE_TEXT (""), + tname, + local_file_count, + local_dir_count)); + ++dir_count; + +#if !defined (ACE_LACKS_CHDIR) +# if (defined (ACE_VXWORKS) && (ACE_VXWORKS < 0x600)) + // Move back to parent directory. + if (ACE_OS::chdir (full_path) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("chdir: %p\n"), + full_path), + -1); +# else + // Move back up a level. + if (ACE_OS::chdir (ACE_TEXT ("..")) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("chdir: %p\n"), + dir_path), + -1); +# endif +#endif /* !ACE_LACKS_CHDIR */ + } + break; + + default: // Must be some other type of file (PIPE/FIFO/device) + file_count++; + break; + } + } + + return entry_count; +} + +static int +dirent_recurse_test (void) +{ + int total_dirs = 0; + int total_files = 0; + const ACE_TCHAR *test_dir = TestDir.c_str (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Starting directory recursion test for %s\n"), + test_dir)); + + if (dirent_count (test_dir, + total_dirs, + total_files, + 1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Directory recursion test failed for %s\n"), + test_dir), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Directory recursion test succeeded for %s, read %d files %d dirs\n"), + test_dir, + total_files, + total_dirs)); + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Dirent_Test")); + + // First, find out where to run most of the scans. If a platform has a + // compiled-in setting in TEST_DIR, honor that. Else, look for: + // $top_srcdir/tests: The ACE_wrappers dir for autoconf builds + // $ACE_ROOT/tests: The ACE_wrappers dir for most other builds + // ../test: Last-chance try to hit the right place +#if defined (TEST_DIR) + TestDir = TEST_DIR; +#else + const char *root = ACE_OS::getenv ("top_srcdir"); + if (root == 0) + root = ACE_OS::getenv ("ACE_ROOT"); + if (root != 0) + { + TestDir = ACE_TEXT_CHAR_TO_TCHAR (root); + TestDir += ACE_DIRECTORY_SEPARATOR_STR; + TestDir += ACE_TEXT ("tests"); + } + else + { + TestDir = ACE_TEXT ("../tests"); + } +#endif /* TEST_DIR */ + + int status = 0; + + if (-1 == dirent_test ()) + status = -1; + + if (-1 == dirent_selector_test ()) + status = -1; + + if (-1 == dirent_recurse_test ()) + status = -1; + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/Dynamic_Priority_Test.cpp b/ACE/tests/Dynamic_Priority_Test.cpp new file mode 100644 index 00000000000..e1ec028c631 --- /dev/null +++ b/ACE/tests/Dynamic_Priority_Test.cpp @@ -0,0 +1,796 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Dynamic_Priority_Test.cpp (based on Priority_Buffer_Test.cpp) +// +// = DESCRIPTION +// This is a test to verify and illustrate the static and dynamic +// priority mechanisms of the <ACE_Message_Queue> class and the +// <ACE_Dynamic_Message_Queue> class. As in the +// <Priority_Buffer_Test>, a producer generates messages and +// enqueues them, and a consumer dequeues them and checks their +// ordering. +// +// In these tests, every effort is made to ensure that there is +// plenty of time for the messages to be enqueued and dequeued, +// with messages that *should* meet their deadlines actually +// meeting them, while messages that should miss their deadlines +// are delayed so that they actually miss them. It is, however, +// remotely possible that this test could yield a false negative: +// the dynamic queues could work correctly but due to timing +// variations the test could indicate failure. +// +// Three message queues are obtained from the message queue +// factory, one static, two dynamic (one deadline based, and one +// laxity based) and the same supplier behavior is used each time: +// the messages are preallocated and their static information +// valued, the current time is obtained and deadlines are set, with +// half of the messages given late deadlines, and the other half of +// the messages given reachable deadlines. The producer then +// immediately enqueues all messages. +// +// Two separate tests are run, one which verifies messages are +// correctly ordered my the given queues, and one which generates +// performance numbers for the various queues under increasing +// numbers of messages. In the first test, the consumer is passed +// the filled queue and a string with the expected order in which +// the messages should dequeue. In the second test, measurements +// are made as non-intrusive as possible, with no ordering checks. +// +// = AUTHOR +// Chris Gill <cdgill@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" /* Include first to enable ACE_ASSERT. */ +#include "ace/Message_Queue.h" +#include "ace/Thread_Manager.h" +#include "ace/High_Res_Timer.h" +#include "ace/Sched_Params.h" + +#if defined (VXWORKS) +# include "ace/OS_NS_string.h" +#endif /* VXWORKS */ + +ACE_RCSID(tests, Dynamic_Priority_Test, "$Id$") + +#if defined (ACE_HAS_TIMED_MESSAGE_BLOCKS) +enum Test_Type {BEST, WORST, RANDOM}; + +class ArgStruct +{ + // = TITLE + // Structure used to pass arguments to test functions. +public: + + ACE_Message_Queue<ACE_SYNCH> *queue_; + // message queue to test + + const char *order_string_; + // string of characters to indicate message order + + ACE_Message_Block **array_; + // array of message blocks to use + + u_int expected_count_; + // expected message count +}; + +// Order in which messages are sent. +static const char send_order [] = "abcdefghijklmnop"; + +// Order in which messages are received with "FIFO prioritization" +// (i.e., none) +// Unused: static const char FIFO_receipt_order [] = "abcdefghijklmnop"; + +// Order in which messages are received with static prioritization. +static const char static_receipt_order [] = "ponmlkjihgfedcba"; + +// Order in which messages are received with deadline prioritization. +static const char deadline_receipt_order [] = "hgfedcbaponmlkji"; + +// Order in which messages are received with laxity prioritization. +static const char laxity_receipt_order [] = "hfgedbcapnomljki"; + +// Fast and slow execution time values (sec, usec), kept very small to +// allow comparison of deadline, laxity, and static strategies across +// a very wide range of message counts. +static const ACE_Time_Value fast_execution (0, 1); +static const ACE_Time_Value slow_execution (0, 2); + +// Make the queue be capable of being *very* large. +static const long max_queue = LONG_MAX; + +#if defined (VXWORKS) +// VxWorks Message Queue parameters. +const int vx_max_queue = INT_MAX; +const int vx_msg_size = 32; +#endif /* defined (VXWORKS) */ + +// Loading parameters (number of messages to push through queues) for +// performance tests. +static int MIN_LOAD = 20; +static int MAX_LOAD = 1000; +static int LOAD_STEP = 20; + +// Time offsets for a minute in the past (for the best case test) and +// two seconds in the future (for the worst case and randomized +// tests). +static const ACE_Time_Value far_past_offset (-60, 0); +static const ACE_Time_Value near_future_offset (2, 0); +static const ACE_Time_Value offset_step (0, 5); + +// The order consumer dequeues a message from the passed +// Message_Queue, and checks its data character against the passed +// string of characters which has the expected ordering. Suppliers +// and consumers do not allocate or deallocate messages, to avoid +// timing delays and timing jitter in the test: the main function is +// responsible for all initialization allocation and cleanup before, +// between, and after (but not during) the transfer of messages from a +// supplier to the corresponding consumer. + +static void * +order_consumer (void *args) +{ + ACE_ASSERT (args != 0); + + ACE_Message_Queue<ACE_SYNCH> *msg_queue = + static_cast<ArgStruct *> (args)->queue_; + const char *receipt_order = + static_cast<ArgStruct *> (args)->order_string_; + u_int expected_count = + static_cast<ArgStruct *> (args)->expected_count_; + + ACE_ASSERT (receipt_order != 0); + ACE_ASSERT (msg_queue != 0); + + u_int local_count = 0; + + // Keep looping, reading a message out of the queue, until we reach + // the end of the receipt order string, which signals us to quit. + + for (const char *expected = receipt_order; + *expected != '\0'; + ++expected) + { + ACE_Message_Block *mb = 0; + + int result = msg_queue->dequeue_head (mb); + + if (result == -1) + break; + + local_count++; + + ACE_ASSERT (*expected == *mb->rd_ptr ()); + } + + ACE_ASSERT (local_count == ACE_OS::strlen (receipt_order)); + ACE_ASSERT (local_count == expected_count); + return 0; +} + +// The order producer runs through the passed send string, setting the +// read pointer of the current message to the current character +// position in the string, and then queueing the message in the +// message list, where it is removed by the order consumer. + +static void * +order_producer (void *args) +{ + ACE_ASSERT (args != 0); + + ACE_Message_Queue<ACE_SYNCH> *msg_queue = + static_cast<ArgStruct *> (args)->queue_; + const char *send_order = + static_cast<ArgStruct *> (args)->order_string_; + ACE_Message_Block **block_array = + static_cast<ArgStruct *> (args)->array_; + int expected_count = + static_cast<ArgStruct *> (args)->expected_count_; + + ACE_ASSERT (send_order != 0); + ACE_ASSERT (block_array != 0); + + // Iterate through the send order string and the message block + // array, setting the current message block's read pointer to the + // current position in the send order string. + int local_count = 0; + const char *c; + + for (local_count = 0, c = send_order; *c != '\0'; ++local_count, ++c) + { + // point to the current message block + ACE_Message_Block *mb = block_array [local_count]; + ACE_ASSERT (mb != 0); + + // Set the current send character in the current message block + // at its read pointer position, and adjust the write pointer. + *mb->wr_ptr () = *c; + mb->wr_ptr (1); + + + // Enqueue the message block in priority order. + if (msg_queue->enqueue_prio (mb) == -1) + break; + } + + ACE_ASSERT (local_count == expected_count); + + return 0; +} + +static int +run_order_test (ACE_Message_Queue<ACE_SYNCH>* msg_queue, + const char *send_order, + const char *receipt_order) +{ + u_int i; + u_int array_size = ACE_OS::strlen (send_order); + + ACE_ASSERT (msg_queue != 0); + ACE_ASSERT (send_order != 0); + ACE_ASSERT (receipt_order != 0); + ACE_ASSERT (ACE_OS::strlen (send_order) == ACE_OS::strlen (receipt_order)); + + ArgStruct supplier_args, consumer_args; + + supplier_args.queue_ = msg_queue; + supplier_args.order_string_ = send_order; + supplier_args.expected_count_ = ACE_OS::strlen (send_order); + + // Allocate message blocks, fill in pointer array, set static + // information. + ACE_NEW_RETURN (supplier_args.array_, + ACE_Message_Block *[array_size], + -1); + + for (i = 0; i < array_size; ++i) + { + // Construct a message new block off the heap, to hold a single + // character. + ACE_NEW_RETURN (supplier_args.array_[i], + ACE_Message_Block (1), + -1); + + // Assign static (minimal) message priority in ascending order. + supplier_args.array_[i]->msg_priority (i); + + // Assign every other message short or long execution time. + supplier_args.array_[i]->msg_execution_time (((i % 2) + ? slow_execution + : fast_execution)); + } + + consumer_args.queue_ = msg_queue; + consumer_args.order_string_ = receipt_order; + consumer_args.expected_count_ = ACE_OS::strlen (receipt_order); + consumer_args.array_ = 0; + + // Construct pending and late absolute deadline times. + + ACE_Time_Value current_time (0, 0); + ACE_Time_Value future_deadline (1, 0); + ACE_Time_Value near_deadline (0, 500000); + ACE_Time_Value recent_deadline (0, -1); + ACE_Time_Value past_deadline (0, -500000); + + current_time = ACE_OS::gettimeofday (); + + future_deadline += current_time; + near_deadline += current_time; + recent_deadline += current_time; + past_deadline += current_time; + + // Set absolute time of deadline associated with the message. + + for (i = 0; i < array_size; ++i) + { + switch ((4 * i) / array_size) + { + case 0: + supplier_args.array_[i]->msg_deadline_time (future_deadline); + break; + case 1: + supplier_args.array_[i]->msg_deadline_time (near_deadline); + break; + case 2: + supplier_args.array_[i]->msg_deadline_time (recent_deadline); + break; + case 3: + supplier_args.array_[i]->msg_deadline_time (past_deadline); + break; + // should never reach here, but its better to make sure + default: + ACE_ASSERT ((4 * i) / array_size < 4); + break; + } + } + + // run the order test producer + order_producer (&supplier_args); + + // run the order test consumer + order_consumer (&consumer_args); + + // free all the allocated message blocks + for (i = 0; i < array_size; ++i) + { + delete supplier_args.array_[i]; + } + + // free the allocated pointer array + delete [] supplier_args.array_; + + return 0; +} + +// The performance consumer starts a timer, dequeues all messages from +// the passed Message_Queue, stops the timer, and reports the number +// of dequeued messages, the elapsed time, and the average time per +// message. + +static void * +performance_consumer (void * args) +{ + ACE_High_Res_Timer timer; + + ACE_ASSERT (args != 0); + + ACE_Message_Queue<ACE_SYNCH> *msg_queue = + static_cast<ArgStruct *> (args)->queue_; + u_int expected_count = + static_cast<ArgStruct *> (args)->expected_count_; + + ACE_ASSERT (msg_queue != 0); + + u_int local_count = 0; + ACE_Message_Block *mb = 0; + + // reset, then start timer + timer.reset (); + timer.start (); + + // Keep looping, reading a message out of the queue, until the + // expected number of messages have been dequeued. + for (local_count = 0; local_count < expected_count; ++local_count) + if (msg_queue->dequeue_head (mb) == -1) + break; + + // Stop timer, obtain and report its elapsed time.x + timer.stop (); + ACE_Time_Value tv; + timer.elapsed_time (tv); + ACE_DEBUG ((LM_INFO, ACE_TEXT ("%6u, %6u, %f"), + local_count, + tv.msec (), + (ACE_timer_t) tv.msec () / local_count)); + + ACE_ASSERT (local_count == expected_count); + return 0; +} + +// The performance producer starts a timer, enqueues the passed +// messages setting the read pointer of each message to the first +// character position in the passed string, stops the timer, and +// reports the number of enqueued messages, the elapsed time, and the +// average time per message. + +static void * +performance_producer (void *args) +{ + ACE_High_Res_Timer timer; + + ACE_ASSERT (args != 0); + + ACE_Message_Queue<ACE_SYNCH> *msg_queue = + static_cast<ArgStruct *> (args)->queue_; + ACE_Message_Block **block_array = + static_cast<ArgStruct *> (args)->array_; + int expected_count = + static_cast<ArgStruct *> (args)->expected_count_; + + ACE_ASSERT (send_order != 0); + ACE_ASSERT (block_array != 0); + + // reset, then start timer + timer.reset (); + timer.start (); + + // Iterate through the message block array, setting the character + // under the current message block's read pointer to null before + // enqueueing the message block. + + int local_count = 0; + for (local_count = 0; local_count < expected_count; ++local_count) + { + // Point to the current message block. + ACE_Message_Block *mb = block_array [local_count]; + ACE_ASSERT (mb != 0); + + // Set a character in the current message block at its + // read pointer position, and adjust the write pointer. + mb->reset(); + *mb->wr_ptr () = 'a'; + mb->wr_ptr (1); + + // Enqueue the message block in priority order. + if (msg_queue->enqueue_prio (mb) == -1) + break; + } + + // Stop timer, obtain and report its elapsed time. + timer.stop (); + ACE_Time_Value tv; + timer.elapsed_time (tv); + ACE_DEBUG ((LM_INFO, ACE_TEXT ("%6u, %6u, %f, "), + local_count, + tv.msec (), + (ACE_timer_t) tv.msec () / local_count)); + + ACE_ASSERT (local_count == expected_count); + return 0; +} + +static int +run_performance_test (u_int min_load, + u_int max_load, + u_int load_step, + Test_Type test_type) +{ + ArgStruct supplier_args, consumer_args; // supplier and consumer argument strings + u_int load = 0; // message load + ACE_Time_Value *time_offsets; // pointer to array of time offsets + ACE_Time_Value current_time; // current time value + u_int shuffle_index; // used to shuffle arrays + int random_int = 0; // also used to shuffle arrays + ACE_Message_Block *temp_block; // temporary message block pointer + ACE_Time_Value temp_time; // temporary time value + + // Build a static queue, a deadline based dynamic queue, and a + // laxity based dynamic queue. + + ACE_Message_Queue<ACE_SYNCH> *static_queue = + ACE_Message_Queue_Factory<ACE_SYNCH>::create_static_message_queue (max_queue); + ACE_ASSERT (static_queue != 0); + + ACE_Message_Queue<ACE_SYNCH> *deadline_queue = + ACE_Message_Queue_Factory<ACE_SYNCH>::create_deadline_message_queue (max_queue); + ACE_ASSERT (deadline_queue != 0); + + ACE_Message_Queue<ACE_SYNCH> *laxity_queue = + ACE_Message_Queue_Factory<ACE_SYNCH>::create_laxity_message_queue (max_queue); + + ACE_ASSERT (laxity_queue != 0); + + // Zero out unused struct members. + supplier_args.order_string_ = 0; + consumer_args.order_string_ = 0; + consumer_args.array_ = 0; + + // Print column headings for the specific test type. + switch (test_type) + { + case BEST: + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\n\nenqueued, best static time, best static avg, ") + ACE_TEXT ("dequeued, best static time, best static avg, ") + ACE_TEXT ("enqueued, best deadline time, best deadline avg, ") + ACE_TEXT ("dequeued, best deadline time, best deadline avg, ") + ACE_TEXT ("enqueued, best laxity time, best laxity avg, ") + ACE_TEXT ("dequeued, best laxity time, best laxity avg\n"))); + break; + case WORST: + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\n\nenqueued, worst static time, worst static avg, ") + ACE_TEXT ("dequeued, worst static time, worst static avg, ") + ACE_TEXT ("enqueued, worst deadline time, worst deadline avg, ") + ACE_TEXT ("dequeued, worst deadline time, worst deadline avg, ") + ACE_TEXT ("enqueued, worst laxity time, worst laxity avg, ") + ACE_TEXT ("dequeued, worst laxity time, worst laxity avg\n"))); + + break; + case RANDOM: + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\n\nenqueued, random static time, random static avg, ") + ACE_TEXT ("dequeued, random static time, random static avg, ") + ACE_TEXT ("enqueued, random deadline time, random deadline avg, ") + ACE_TEXT ("dequeued, random deadline time, random deadline avg, ") + ACE_TEXT ("enqueued, random laxity time, random laxity avg, ") + ACE_TEXT ("dequeued, random laxity time, random laxity avg\n"))); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unknown test type %d"), + test_type), + -1); + } + + // Iterate through the message loads, and at each load do an + // identical test on all queues. + for (load = min_load; load <= max_load; load += load_step) + { + u_int i; + + supplier_args.expected_count_ = load; + consumer_args.expected_count_ = load; + + // Allocate message blocks, fill in pointer array, set static + // information. + ACE_NEW_RETURN (supplier_args.array_, + ACE_Message_Block *[load], + -1); + + // Allocate array of timing offsets. + ACE_NEW_RETURN (time_offsets, + ACE_Time_Value [load], + -1); + + // Fill in information for all types of tests. + for (i = 0; i < load; ++i) + { + // Construct a message new block off the heap, to hold a + // single character. + ACE_NEW_RETURN (supplier_args.array_[i], + ACE_Message_Block (1), + -1); + + // Assign every other message short or long execution time. + supplier_args.array_[i]->msg_execution_time (((i % 2) + ? slow_execution + : fast_execution)); + } + + // Fill in information for the specific type of test. + switch (test_type) + { + case BEST: + // Fill in best case information. + time_offsets [0] = far_past_offset; + supplier_args.array_[0]->msg_priority (load); + + for (i = 1; i < load; ++i) + { + // Assign static (minimal) message priority in + // descending order. + supplier_args.array_[i]->msg_priority (load - i); + + // Assign time to deadline in descending order. + time_offsets [i] = time_offsets [i - 1] + offset_step; + } + + break; + case WORST: + // Fill in worst case information. + time_offsets [0] = near_future_offset; + supplier_args.array_[0]->msg_priority (0); + + for (i = 1; i < load; ++i) + { + // Assign static (minimal) message priority in ascending + // order. + supplier_args.array_[i]->msg_priority (i); + + // Assign time to deadline in descending order (puts + // dynamic priority in ascending order). + time_offsets [i] = time_offsets [i - 1] - offset_step; + } + break; + case RANDOM: + // Fill in worst case information. + time_offsets [0] = near_future_offset; + supplier_args.array_[0]->msg_priority (0); + + for (i = 1; i < load; ++i) + { + // Assign static (minimal) message priority in ascending + // order. + supplier_args.array_[i]->msg_priority (i); + + // Assign time to deadline in descending order (puts + // dynamic priority in ascending order). + time_offsets [i] = time_offsets [i - 1] - offset_step; + } + + // Then shuffle the arrays in tandem. + for (i = 0; i < load; ++i) + { + // Choose a (pseudo) random integer (evenly distributed + // over [0, load-1]). + if (RAND_MAX >= load) + { + // Discard integers in the tail of the random range + // that do not distribute evenly modulo the number + // of messages. + do + random_int = ACE_OS::rand (); + while (random_int >= (int)(RAND_MAX - (RAND_MAX % load))); + } + else if (RAND_MAX < load - 1) + // This should only happen for a *very* large messages + // relative to the system's representation size. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Insufficient range of random numbers")), + -1); + shuffle_index = random_int % load; + + // Swap the message at the current index with the one at + // the shuffle index. + temp_block = supplier_args.array_[i]; + supplier_args.array_[i] = supplier_args.array_[shuffle_index]; + supplier_args.array_[shuffle_index] = temp_block; + + // Swap the time at the current index with the one at + // the shuffle index. + temp_time = time_offsets [i]; + time_offsets [i] = time_offsets [shuffle_index]; + time_offsets [shuffle_index] = temp_time; + } + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unknown test type %d"), + test_type), + -1); + } + + // Set absolute time of deadline associated with each message. + current_time = ACE_OS::gettimeofday (); + + for (i = 0; i < load; ++i) + supplier_args.array_[i]->msg_deadline_time (time_offsets [i] + current_time); + + // Run the performance test producer and consumer on the static + // queue. + supplier_args.queue_ = static_queue; + performance_producer (&supplier_args); + consumer_args.queue_ = static_queue; + performance_consumer (&consumer_args); + + // Add a comma delimiter for most recent outputs. + ACE_DEBUG ((LM_INFO, + ACE_TEXT (", "))); + + // Run the performance test producer and consumer on the + // deadline queue. + supplier_args.queue_ = deadline_queue; + performance_producer (&supplier_args); + consumer_args.queue_ = deadline_queue; + performance_consumer (&consumer_args); + + // Add a comma delimiter for most recent outputs. + ACE_DEBUG ((LM_INFO, + ACE_TEXT (", "))); + + // Run the performance test producer and consumer on the laxity + // queue. + supplier_args.queue_ = laxity_queue; + performance_producer (&supplier_args); + consumer_args.queue_ = laxity_queue; + performance_consumer (&consumer_args); + + // Move to the next line of output. + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\n"))); + + delete [] time_offsets; + + // Free all the allocated message blocks. + for (i = 0; i < load; ++i) + delete supplier_args.array_[i]; + + // Free the allocated pointer array. + delete [] supplier_args.array_; + + } + + // Free resources and leave. + delete static_queue; + delete deadline_queue; + delete laxity_queue; + return 0; +} +#endif /* ACE_HAS_TIMED_MESSAGE_BLOCKS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Dynamic_Priority_Test")); + +#if defined (ACE_HAS_TIMED_MESSAGE_BLOCKS) + // Enable FIFO scheduling, e.g., RT scheduling class on Solaris. + if (ACE_OS::sched_params ( + ACE_Sched_Params ( + ACE_SCHED_FIFO, + ACE_Sched_Params::priority_min (ACE_SCHED_FIFO), + ACE_SCOPE_PROCESS)) != 0) + { + if (ACE_OS::last_error () == EPERM) + ACE_DEBUG ((LM_MAX, + ACE_TEXT ("user is not superuser, ") + ACE_TEXT ("so remain in time-sharing class\n"))); + else if (ACE_OS::last_error () == ENOTSUP) + ACE_DEBUG ((LM_MAX, + ACE_TEXT ("process scope scheduling is not available, ") + ACE_TEXT ("so remain in time-sharing class\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%n: ACE_OS::sched_params failed\n%a")), + -1); + } + + // Test factory, static message queue. + ACE_Message_Queue<ACE_SYNCH> *test_queue = + ACE_Message_Queue_Factory<ACE_SYNCH>::create_static_message_queue (max_queue); + ACE_ASSERT (test_queue != 0); + run_order_test (test_queue, + send_order, + static_receipt_order); + delete test_queue; + + // Test factory, dynamic message queue (deadline strategy). + test_queue = + ACE_Message_Queue_Factory<ACE_SYNCH>::create_deadline_message_queue (max_queue); + ACE_ASSERT (test_queue != 0); + run_order_test (test_queue, + send_order, + deadline_receipt_order); + delete test_queue; + + // Test factory, dynamic message queue (laxity strategy). + test_queue = + ACE_Message_Queue_Factory<ACE_SYNCH>::create_laxity_message_queue (max_queue); + ACE_ASSERT (test_queue != 0); + run_order_test (test_queue, + send_order, + laxity_receipt_order); + delete test_queue; + +#if defined (VXWORKS) + // test factory for VxWorks message queue. + ACE_Message_Queue_Vx *test_queue_vx = + ACE_Message_Queue_Factory<ACE_NULL_SYNCH>::create_Vx_message_queue (vx_max_queue, + vx_msg_size); + ACE_ASSERT (test_queue_vx != 0); + // (TBD - does message receipt order test make any sense for Vx Queue ? + // If so, uncomment order test, or if not remove order test, below) + // @@ % levine 22 Jul 1998 % It'd be nice to run the test, but: + // ACE_Message_Queue_Vx isa + // ACE_Message_Queue<ACE_NULL_SYNCH>, not an + // ACE_Message_Queue<ACE_MT_SYNCH>, so we're + // not type-compatible. + + // run_order_test (test_queue, send_order, static_receipt_order); + delete test_queue_vx; +#endif /* VXWORKS */ + + // For each of an increasing number of message loads, run the same + // performance test (best case, worst case, and randomized, over + // each kind of queue). + run_performance_test (MIN_LOAD, + MAX_LOAD, + LOAD_STEP, + BEST); + + run_performance_test (MIN_LOAD, + MAX_LOAD, + LOAD_STEP, + WORST); + + run_performance_test (MIN_LOAD, + MAX_LOAD, + LOAD_STEP, + RANDOM); +#else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE is not compiled with ACE_HAS_TIMED_MESSAGE_BLOCKS enabled\n"))); +#endif /* ACE_HAS_TIMED_MESSAGE_BLOCKS */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Dynamic_Test.cpp b/ACE/tests/Dynamic_Test.cpp new file mode 100644 index 00000000000..9071c5f57eb --- /dev/null +++ b/ACE/tests/Dynamic_Test.cpp @@ -0,0 +1,151 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Dynamic_Test.cpp +// +// = DESCRIPTION +// This tests the ACE_Dynamic class +// +// = AUTHORS +// Johnny Willemsen <jwillemsen@remedy.nl> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Dynamic.h" +#include "ace/OS_Memory.h" + +ACE_RCSID(tests, Dynamic_Test, "$Id$") + +class A +{ +public: + A (); + + void *operator new (size_t n); + +#if defined (ACE_HAS_NEW_NOTHROW) + void *operator new (size_t n, const ACE_nothrow_t&) throw(); +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) + void operator delete (void *p, const ACE_nothrow_t&) throw (); +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ +#endif + + void * operator new (size_t n, void *p); + + void operator delete (void *); + +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) + void operator delete (void *, void *); +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ + + /// Have we been dynamically created? + bool dynamic_; +}; + +void* +A::operator new (size_t n) +{ + ACE_Dynamic *const dynamic_instance = ACE_Dynamic::instance (); + + if (dynamic_instance == 0) + { + // If this ACE_ASSERT fails, it may be due to running of out TSS + // keys. Try using ACE_HAS_TSS_EMULATION, or increasing + // ACE_DEFAULT_THREAD_KEYS if already using TSS emulation. + ACE_ASSERT (dynamic_instance != 0); + + ACE_throw_bad_alloc; + } + else + { + // Allocate the memory and store it (usually in thread-specific + // storage, depending on config flags). + dynamic_instance->set (); + + return ::new char[n]; + } +} + +#if defined (ACE_HAS_NEW_NOTHROW) +void* +A::operator new (size_t n, const ACE_nothrow_t&) throw() +{ + ACE_Dynamic *const dynamic_instance = ACE_Dynamic::instance (); + + if (dynamic_instance == 0) + { + // If this ACE_ASSERT fails, it may be due to running of out TSS + // keys. Try using ACE_HAS_TSS_EMULATION, or increasing + // ACE_DEFAULT_THREAD_KEYS if already using TSS emulation. + ACE_ASSERT (dynamic_instance != 0); + + return 0; + } + else + { + // Allocate the memory and store it (usually in thread-specific + // storage, depending on config flags). + dynamic_instance->set (); + + return ::new(ACE_nothrow) char[n]; + } +} + +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) +void +A::operator delete (void *p, const ACE_nothrow_t&) throw() +{ + ::delete [] static_cast <char *> (p); +} +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ + +#endif /* ACE_HAS_NEW_NOTHROW */ + +void +A::operator delete (void *obj) +{ + ::delete [] static_cast <char *> (obj); +} + +A::A() +{ + this->dynamic_ = ACE_Dynamic::instance ()->is_dynamic (); + + if (this->dynamic_) + // Make sure to reset the flag. + ACE_Dynamic::instance ()->reset (); +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Dynamic_Test")); + A from_stack; + A* heap = 0; + ACE_NEW_RETURN (heap, A, 1); + if (from_stack.dynamic_) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("dynamic_ is true for an object on the stack\n")), + 1); + } + + if (!heap->dynamic_) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("dynamic_ is false for an object from the heap\n")), + 1); + } + + delete heap; + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Enum_Interfaces_Test.cpp b/ACE/tests/Enum_Interfaces_Test.cpp new file mode 100644 index 00000000000..4098387cf5e --- /dev/null +++ b/ACE/tests/Enum_Interfaces_Test.cpp @@ -0,0 +1,96 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Enum_interfaces.cpp +// +// = DESCRIPTION +// This is a simple test of <ACE::get_ip_interfaces>. This call +// retrieves the IP addresses assigned to the host by +// interrogating the kernel. Network applications typically +// assume gethostbyname(uname()) will work, but this is just a +// convention. It is also problematic if the resolver code +// (DNS/NIS+...) is misconfigured. This happens more than +// programmers realize. It is better to find out by asking the +// kernel for local address assignments. This API is similar to +// what netstat -ni or ifconfig -a produces on UNIX or ipconfig on +// Windows NT. In fact, it was by reverse engineering these tools +// that this api was created. +// +// = AUTHOR +// Michael R. MacFaden <mrm@cisco.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_sys_utsname.h" +#include "ace/INET_Addr.h" + +ACE_RCSID(tests, Enum_Interfaces_Test, "$Id$") + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Enum_Interfaces_Test")); + + ACE_utsname uname; + ACE_OS::uname (&uname); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Machine: %C running on %C\n"), + uname.nodename, uname.machine )); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Platform: %C, %C, %C\n"), + uname.sysname, uname.release, uname.version )); + + ACE_INET_Addr *the_addr_array; + size_t how_many = 0; + + int rc = ACE::get_ip_interfaces (how_many, the_addr_array); + + if (rc != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE::get_ip_interfaces failed"))); + else if (how_many == 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("No interfaces presently configured in the kernel\n"))); + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" there are %d interfaces\n"), how_many)); + int num_ipv4 = 0; + int num_ipv6 = 0; + for (size_t i = 0; i < how_many; i++) + { + if (the_addr_array[i].get_type() == AF_INET) + ++num_ipv4; +#if defined (ACE_HAS_IPV6) + else if (the_addr_array[i].get_type() == AF_INET6) + ++num_ipv6; +#endif /* ACE_HAS_IPV6 */ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\t%s\n"), + ACE_TEXT_CHAR_TO_TCHAR (the_addr_array[i].get_host_addr ()))); + } + + delete [] the_addr_array; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" there are %d IPv4 interfaces, ") + ACE_TEXT ("and %d IPv6 interfaces\n"), + num_ipv4, num_ipv6)); +#if defined (ACE_HAS_IPV6) + if (num_ipv6 == 0) + { + rc = 1; + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("Since ACE_HAS_IPV6 is set, at least 1 ") + ACE_TEXT ("ipv6 interface was expected\n"))); + } +#endif /* ACE_HAS_IPV6 */ + } + + ACE_END_TEST; + return rc != 0; // return 1 if get_ip_interfaces() failed +} diff --git a/ACE/tests/Env_Value_Test.cpp b/ACE/tests/Env_Value_Test.cpp new file mode 100644 index 00000000000..5cb8464b55b --- /dev/null +++ b/ACE/tests/Env_Value_Test.cpp @@ -0,0 +1,126 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This is a simple test to make sure the Env_Value template is +// working correctly. +// +// = AUTHOR +// Chris Cleeland <cleeland@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_string.h" +#include "ace/Process.h" +#include "ace/Env_Value_T.h" + +ACE_RCSID(tests, Env_Value_Test, "$Id$") + +#define TEST_THIS(type, varname, defval, expval) \ +do { \ + ACE_Env_Value<type> val (varname, (defval)); \ + if (val != (expval)) { \ + ACE_ERROR ((LM_ERROR, \ + ACE_TEXT ("val %d does not match expected value of %d\n"), \ + (int) (type) val, (int) (expval))); \ + } \ +} while (0) + +int +// This has been unconditionally turned on for the time being since I can't +// figure out an easy way to enable it and still keep ACE_TMAIN in a seperate +// cpp. +#if 1 || defined (ACE_HAS_NONSTATIC_OBJECT_MANAGER) || defined (ACE_LACKS_FORK) +// ACE_HAS_NONSTATIC_OBJECT_MANAGER only allows main to have two +// arguments. And on platforms that lack fork (), we can't use spawn. +run_main (int argc, ACE_TCHAR* []) +{ + ACE_UNUSED_ARG (argc); + + // Only Win32 can set wide-char environment strings. So, for all + // others, use char string literals regardless of ACE_USES_WCHAR. +# if defined (ACE_WIN32) + ACE_OS::putenv (ACE_TEXT ("TEST_VALUE_POSITIVE=10.2")); + ACE_OS::putenv (ACE_TEXT ("TEST_VALUE_NEGATIVE=-10.2")); +# else + ACE_OS::putenv ("TEST_VALUE_POSITIVE=10.2"); + ACE_OS::putenv ("TEST_VALUE_NEGATIVE=-10.2"); +# endif /* ACE_WIN32 */ +#else /* ! ACE_HAS_NONSTATIC_OBJECT_MANAGER && ! ACE_LACKS_FORK */ +run_main (int argc, ACE_TCHAR * [], ACE_TCHAR *envp[]) +{ + if (argc == 1) + { + int status; + + // No arguments means we're the initial test. + ACE_Process_Options options (1); + status = options.setenv (envp); + if (status != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("setenv(envp)"))); + + options.command_line (ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("Env_Value_Test run_as_test")); + + status = options.setenv (ACE_TEXT ("TEST_VALUE_POSITIVE"), + ACE_TEXT ("%s"), + ACE_TEXT ("10.2")); + if (status != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("setenv(TEST_VALUE_POSITIVE)"))); + + status = options.setenv (ACE_TEXT ("TEST_VALUE_NEGATIVE"), + ACE_TEXT ("%s"), + ACE_TEXT ("-10.2")); + if (status != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("setenv(TEST_VALUE_NEGATIVE)"))); + + ACE_Process p; + pid_t result = p.spawn (options); + if (result == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn"))); + else + p.wait (); + } + else +#endif /* ! ACE_HAS_NONSTATIC_OBJECT_MANAGER && ! ACE_LACKS_FORK */ + { + // In this case we're the child + + ACE_START_TEST (ACE_TEXT ("Env_Value_Test")); + + TEST_THIS (int, ACE_TEXT ("TEST_VALUE_POSITIVE"), 4, 10); + TEST_THIS (double, ACE_TEXT ("TEST_VALUE_POSITIVE"), -1.0, 10.2); + TEST_THIS (long, ACE_TEXT ("TEST_VALUE_POSITIVE"), 0, 10); + TEST_THIS (unsigned long, ACE_TEXT ("TEST_VALUE_POSITIVE"), 0, 10); + TEST_THIS (short, ACE_TEXT ("TEST_VALUE_POSITIVE"), 0, 10); + TEST_THIS (unsigned short, ACE_TEXT ("TEST_VALUE_POSITIVE"), 0, 10); + + TEST_THIS (int, ACE_TEXT ("TEST_VALUE_NEGATIVE"), 4, -10); + TEST_THIS (double, ACE_TEXT ("TEST_VALUE_NEGATIVE"), -1.0, -10.2); + TEST_THIS (long, ACE_TEXT ("TEST_VALUE_NEGATIVE"), 0, -10L); + TEST_THIS (unsigned long, ACE_TEXT ("TEST_VALUE_NEGATIVE"), 0, (unsigned long) -10); + TEST_THIS (short, ACE_TEXT ("TEST_VALUE_NEGATIVE"), 0, -10); + TEST_THIS (unsigned short, ACE_TEXT ("TEST_VALUE_NEGATIVE"), 0, (unsigned short) -10); + + const ACE_TCHAR *defstr = ACE_TEXT ("Sarah Cleeland is Two!"); + ACE_Env_Value<const ACE_TCHAR *> sval (ACE_TEXT ("This_Shouldnt_Be_Set_Hopefully"), + defstr); + if (ACE_OS::strcmp (sval, defstr) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Mismatch: %s should be %s\n"), + (const ACE_TCHAR *)sval, defstr)); + ACE_END_TEST; + } + return 0; +} + diff --git a/ACE/tests/FIFO_Test.cpp b/ACE/tests/FIFO_Test.cpp new file mode 100644 index 00000000000..8a7f7a64a44 --- /dev/null +++ b/ACE/tests/FIFO_Test.cpp @@ -0,0 +1,311 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// FIFO_Test.cpp +// +// = DESCRIPTION +// This is a test of basic ACE_FIFO_* class functionality. +// The test forks two processes or spawns two threads (depending +// upon the platform) and then tests various data exchange +// scenarios. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" +#include "ace/ACE.h" +#include "ace/FIFO_Send_Msg.h" +#include "ace/FIFO_Recv_Msg.h" +#include "ace/Handle_Set.h" +#include "ace/Lib_Find.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" + +ACE_RCSID(tests, SOCK_Test, "$Id$") + +#if !defined (ACE_LACKS_MKFIFO) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// This length is used for the "big buffer" send/receive. +static const size_t big_size = (BUFSIZ * 4); + +static void * +client (void *arg) +{ + ACE_TCHAR *fifo_path = reinterpret_cast <ACE_TCHAR *> (arg); + ACE_FIFO_Send_Msg fifo; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) client opening %s\n"), fifo_path)); + if (fifo.open (fifo_path) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), fifo_path), 0); + + // Try some transfers - the server part is expecting this data. + // First, try a nice, easy send. + ssize_t send_count; + ssize_t expect = static_cast <ssize_t> (ACE_OS::strlen (ACE_ALPHABET)); + send_count = fifo.send (ACE_ALPHABET, ACE_OS::strlen (ACE_ALPHABET)); + if (send_count == expect) + { + // Ok, so far so good. Now try one that will overflow the reader + // side to be sure it properly tosses the overflow. Then send another + // to be sure it finds the start of the next message ok. + char big[big_size]; + for (size_t i = 0; i < big_size; ++i) + big[i] = (i % 2) ? 0x05 : 0x0A; // Make nice pattern in blown stack + expect = static_cast <ssize_t> (big_size); + send_count = fifo.send (big, big_size); + if (send_count == expect) + { + expect = static_cast <ssize_t> (ACE_OS::strlen (ACE_ALPHABET)); + send_count = fifo.send (ACE_ALPHABET, ACE_OS::strlen (ACE_ALPHABET)); + if (send_count != expect) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Final send; sent %d, expected %d") + ACE_TEXT ("%p\n"), + send_count, expect, ACE_TEXT ("send"))); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) All sends ok\n"))); + } + else + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("(%P|%t) Second send expected %d; sent %d. %p\n"), + expect, send_count, ACE_TEXT ("send"))); + } + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) First send expected %d; sent %d. %p\n"), + expect, send_count, ACE_TEXT ("send"))); + } + + if (fifo.close () != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("fifo close"))); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_FIFO_Recv_Msg *fifo = reinterpret_cast <ACE_FIFO_Recv_Msg *> (arg); + + // Wait for the client to get going and open the FIFO. + errno = 0; + ACE_Handle_Set h; + ACE_Time_Value delay (10); + h.set_bit (fifo->get_handle ()); + if (ACE::select (h.max_set () + 1, h, &delay) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) server %p\n"), + ACE_TEXT ("select")), + 0); + + // On AIX, select() always seems to select a fifo handle as a normal file, + // always readable. Just wait a second... +# if defined (AIX) || defined (HPUX) || defined (__osf__) + ACE_OS::sleep (1); +# endif /* AIX || HPUX */ + + // Read the things the client is sending; alphabet, huge overflow, then + // alphabet. + + char buf[BUFSIZ]; + ssize_t recv_count; + ssize_t expect = static_cast <ssize_t> (ACE_OS::strlen (ACE_ALPHABET)); + recv_count = fifo->recv (buf, sizeof (buf)); + if (recv_count != expect) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Recv 1 expected %d, got %d. %p\n"), + expect, recv_count, ACE_TEXT ("recv")), + 0); + buf[recv_count] = '\0'; + if (ACE_OS::strcmp (buf, ACE_ALPHABET) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Recv 1 expected alphabet; got %s\n"), + buf)); + + // See documented return values for ACE_FIFO_Recv_Msg... + // We are being sent a message much longer than BUFSIZ. + // If this platform has STREAM pipes, the entire message will come + // through and we can grab it all. If not, then ACE_FIFO_Recv_Msg ditches + // the part of the message we don't read. This is rather a pain in the + // neck, but the API doesn't return info that more data is in the message + // (for STREAM pipes). When non-ACE_HAS_STREAM_PIPES discards data, the + // returned length will be larger than requested, though only the requested + // number of bytes are written to the buffer. +#if defined (ACE_HAS_STREAM_PIPES) + for (size_t remaining = big_size; + remaining > 0; + remaining -= recv_count) + { +#endif /* ACE_HAS_STREAM_PIPES */ + + // recv_count is sizeof(buf) on ACE_HAS_STREAM_PIPES; big_size on others. +#if defined (ACE_HAS_STREAM_PIPES) + expect = static_cast <ssize_t> (sizeof (buf)); +#else + expect = static_cast <ssize_t> (big_size); +#endif /* ACE_HAS_STREAM_PIPES */ + recv_count = fifo->recv (buf, sizeof (buf)); + if (recv_count != expect) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Recv 2 expected %d, ") + ACE_TEXT ("got %d. %p\n"), + expect, recv_count, ACE_TEXT ("recv")), + 0); +#if defined (ACE_HAS_STREAM_PIPES) + } +#endif /* ACE_HAS_STREAM_PIPES */ + + expect = static_cast <ssize_t> (ACE_OS::strlen (ACE_ALPHABET)); + recv_count = fifo->recv (buf, sizeof (buf)); + if (recv_count != expect) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Recv 3 expected %d, got %d. %p\n"), + expect, recv_count, ACE_TEXT ("recv")), + 0); + buf[recv_count] = '\0'; + if (ACE_OS::strcmp (buf, ACE_ALPHABET) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Recv 3 expected alphabet; got %s\n"), + buf)); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) All receives ok\n"))); + return 0; +} + +static int +test_fifo_msg (void) +{ + // Reader side opens first - it may fail if fifo not supported on this + // platform. + ACE_TCHAR fifo_path[MAXPATHLEN]; + if (ACE::get_temp_dir (fifo_path, MAXPATHLEN) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("get_temp_dir")), 1); + ACE_OS::strcat (fifo_path, ACE_TEXT ("FIFO_Test")); + ACE_FIFO_Recv_Msg read_side; + // Open read only, not persistent (4th arg is 0) + if (-1 == read_side.open (fifo_path, + O_CREAT | O_RDONLY, + ACE_DEFAULT_FILE_PERMS, + 0)) + { +#if defined (ACE_WIN32) + if (errno == ENOTSUP) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("FIFO not supported on Win32; ") + ACE_TEXT ("this is correct.\n"))); + return 0; + } +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_FIFO_Recv_Msg::open")), + 1); + } + + // Ok, the FIFO opened clean for read. Now it's safe to spawn a + // process/thread and test some transfers. + + int status = 0; // Test status; innocent until proven guilty. + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("fork failed"))); + status = 1; + break; + case 0: + client (fifo_path); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server (&read_side); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + &read_side, + THR_NEW_LWP | THR_DETACHED) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("server spawn"))); + ++status; + } + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + fifo_path, + THR_NEW_LWP | THR_DETACHED) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("client spawn"))); + ++status; + } + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("This test requires multiple threads ") + ACE_TEXT ("or processes.\n"))); +#endif /* ACE_HAS_THREADS */ + + if (read_side.remove () != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("fifo remove"))); + ++status; + } + + ACE_stat fifo_stat; + if (ACE_OS::stat (fifo_path, &fifo_stat) == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("FIFO %s should be gone but isn't\n"), + fifo_path)); + ++status; + ACE_OS::unlink (fifo_path); // Try to get rid of it. + } + + return status; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("FIFO_Test")); + + int errors = 0; + errors += test_fifo_msg (); + + ACE_END_TEST; + return errors; +} + +#else + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("FIFO_Test")); + + ACE_ERROR ((LM_INFO, ACE_TEXT ("FIFOs are not supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} + +#endif /* !ACE_LACKS_MKFIFO */ diff --git a/ACE/tests/FlReactor_Test.cpp b/ACE/tests/FlReactor_Test.cpp new file mode 100644 index 00000000000..9532b6e0a85 --- /dev/null +++ b/ACE/tests/FlReactor_Test.cpp @@ -0,0 +1,278 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// FlReactor_Test.cpp +// +// = DESCRIPTION +// A simple test that ilustrates the integration of the fast-light +// toolkit (http://fltk.easysw.org/) with ACE, it uses FL to create +// an OpenGL window and display a polygon, it uses ACE to open an +// acceptor. Every time there is a connection the number of polygons +// is increased, a little widget can be used to change the number of +// polygons too. +// +// = AUTHOR +// Carlos O'Ryan <coryan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID(tests, FlReactor_Test, "$Id$") + + +#include "ace/FlReactor/FlReactor.h" +#include "ace/Event_Handler.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Service_Config.h" +#include "ace/Thread_Manager.h" + +#include /**/ <FL/Fl.h> +#include /**/ <FL/Fl_Window.h> +#include /**/ <FL/Fl_Hor_Slider.h> +#include /**/ <FL/Fl_Box.h> +#include /**/ <FL/math.h> +#include /**/ <FL/gl.h> +#include /**/ <FL/Fl_Gl_Window.h> + +class Test_Window : public Fl_Gl_Window +{ +public: + Test_Window (int x, int y, int w, int h, + const char * l = 0); + // Constructor + + int sides (void) const; + void sides (int s); + void incr_sides (void); + +private: + virtual void draw (void); + // from the Fl_Gl_Window... + + int sides_; +}; + +Test_Window::Test_Window (int x, int y, + int w, int h, + const char* l) + : Fl_Gl_Window (x, y, w, h, l), + sides_ (3) +{ +} + +int +Test_Window::sides (void) const +{ + return this->sides_; +} + +void +Test_Window::sides (int s) +{ + this->sides_ = s; + this->redraw (); +} + +void +Test_Window::incr_sides (void) +{ + this->sides_++; + if (this->sides_ > 10) + this->sides_ = 3; + this->redraw (); +} + +void +Test_Window::draw (void) +{ + // the valid() property may be used to avoid reinitializing your + // GL transformation for each redraw: + if (!this->valid ()) + { + this->valid (1); + glLoadIdentity (); + glViewport (0, 0, this->w (), this->h ()); + } + // draw an amazing but slow graphic: + glClear (GL_COLOR_BUFFER_BIT); + + glBegin (GL_POLYGON); + int s = this->sides_; + + for (int i = 0; i != s; ++i) + { + double ang = i * 2 * M_PI / s; + glColor3f (float (i) / s, + float (i) / s, + float (i) / s); + glVertex3f (cos (ang), sin (ang), 0); + } + glEnd (); +} + +// when you change the data, as in this callback, you must call redraw (): +void sides_cb (Fl_Widget *o, void *p) +{ + Test_Window *tw = + reinterpret_cast<Test_Window *> (p); + Fl_Slider *slider = + dynamic_cast<Fl_Slider*> (o); + tw->sides (static_cast<int> (slider->value ())); +} + +class Connection_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + Connection_Handler (Test_Window *w = 0, + Fl_Box* box = 0); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void *); + //FUZZ: enble check_for_lack_ACE_OS + + virtual int handle_input (ACE_HANDLE); + +private: + Test_Window *w_; + Fl_Box *box_; +}; + +class Acceptor : public ACE_Acceptor<Connection_Handler,ACE_SOCK_ACCEPTOR> +{ +public: + Acceptor (Test_Window *w = 0, + Fl_Box *box = 0); + + virtual int make_svc_handler (Connection_Handler *&sh); + +private: + Test_Window* w_; + Fl_Box *box_; +}; + +Connection_Handler::Connection_Handler (Test_Window *w, + Fl_Box *box) + : w_ (w), + box_ (box) +{ +} + +int +Connection_Handler::open (void*) +{ + if (this->box_ != 0) + { + ACE_INET_Addr from; + + this->peer ().get_remote_addr (from); + const int bufsiz = 128; + char buf[bufsiz]; + + from.addr_to_string (buf, bufsiz, 0); + + static char msg[256]; + ACE_OS::sprintf (msg, "connection from <%s>\n", buf); + + this->box_->label (msg); + this->box_->redraw (); + } + + if (this->w_ != 0) + { + this->w_->incr_sides (); + } + + return this->peer ().enable (ACE_NONBLOCK); +} + +int +Connection_Handler::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + if (this->peer ().recv (buf, BUFSIZ) <= 0) + return -1; + return 0; +} + +Acceptor::Acceptor (Test_Window *w, Fl_Box *box) + : w_ (w), + box_ (box) +{ +} + +int +Acceptor::make_svc_handler (Connection_Handler *&sh) +{ + if (sh == 0) + { + ACE_NEW_RETURN (sh, Connection_Handler (this->w_, this->box_), -1); + sh->reactor (this->reactor()); + } + return 0; +} + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("FlReactor_Test")); + + Fl_Window window (300, 370); + + Test_Window tw (10, 75, window.w () - 20, window.h ()-90); + window.resizable (&tw); + + Fl_Hor_Slider slider (60, 5, window.w () - 70, 30, "Sides:"); + slider.align (FL_ALIGN_LEFT); + slider.callback (sides_cb, &tw); + slider.value (tw.sides ()); + slider.step (1); + slider.bounds (3, 10); + + ACE_FlReactor reactor; + ACE_Reactor r (&reactor); + + Fl_Box *box = new Fl_Box (FL_UP_BOX, 10, 40, + window.w () - 20, 30, + "Setting up"); + box->labelfont (FL_BOLD); + + Acceptor acceptor (&tw, box); + + ACE_INET_Addr address; + + if (acceptor.open (address, &r) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open acceptor"), + -1); + + acceptor.acceptor ().get_local_addr (address); + + const int bufsiz = 128; + char buf[bufsiz]; + + address.addr_to_string (buf, bufsiz, 0); + + char msg[2 * bufsiz]; + ACE_OS::sprintf (msg, "Listening on <%s>\n", buf); + + box->label (msg); + box->redraw (); + + window.end (); + window.show (argc, argv); + tw.show (); + + return Fl::run (); + + ACE_END_TEST; +} + diff --git a/ACE/tests/Framework_Component_DLL.cpp b/ACE/tests/Framework_Component_DLL.cpp new file mode 100644 index 00000000000..f8e4e34dbf0 --- /dev/null +++ b/ACE/tests/Framework_Component_DLL.cpp @@ -0,0 +1,82 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Framework_Component_DLL.cpp +// +// = DESCRIPTION +// This is a simple server that can be loaded via the ACE +// Service Configuration framework and uses the singleton that +// also lives in this library. +// +// = AUTHOR +// Don Hinton <dhinton@ieee.org> +// +// ============================================================================ + +#include "Framework_Component_DLL.h" +#include "ace/Service_Config.h" +#include "ace/Service_Object.h" +#include "ace/Framework_Component_T.h" + +ACE_RCSID (tests, + Framework_Component_DLL, + "$Id$") + +ACE_DLL_UNLOAD_POLICY (Framework_Component_DLL, ACE_DLL_UNLOAD_POLICY_LAZY) + +Simple_Service::Simple_Service (void) +{ + FRAMEWORK_COMPONENT_DLL_TRACE ("Simple_Service::Simple_Service"); +} + +Simple_Service::~Simple_Service (void) +{ + FRAMEWORK_COMPONENT_DLL_TRACE ("Simple_Service::~Simple_Service"); +} + +const ACE_TCHAR * +Simple_Service::name (void) +{ + FRAMEWORK_COMPONENT_DLL_TRACE ("Simple_Service::dll_name"); + return ACE_TEXT ("Simple_Service"); +} + +/***************************************************************************/ + +template <int which> +class Server_T : public ACE_Service_Object +{ +public: + int init (int, ACE_TCHAR *[]) + { + FRAMEWORK_COMPONENT_DLL_TRACE ("Server_T::init"); + + // Initialize the singleton + FWCT_DLL_Singleton_Adapter_T <Simple_Service> *ss = SS_SINGLETON::instance (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Server_T::init() dll_name: %s\n"), + ss->dll_name ())); + return 0; + } + + int fini (void) + { + FRAMEWORK_COMPONENT_DLL_TRACE ("Server_T::fini"); + return 0; + } +}; + +typedef Server_T <1> Server_1; +FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARATION(Server_T<1>;) +ACE_FACTORY_DEFINE (Framework_Component_DLL, Server_1) + +typedef Server_T <2> Server_2; +FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARATION(Server_T<2>;) +ACE_FACTORY_DEFINE (Framework_Component_DLL, Server_2) + diff --git a/ACE/tests/Framework_Component_DLL.h b/ACE/tests/Framework_Component_DLL.h new file mode 100644 index 00000000000..c9760b33dcb --- /dev/null +++ b/ACE/tests/Framework_Component_DLL.h @@ -0,0 +1,58 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Framework_Component_DLL.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Don Hinton <dhinton@ieee.org> +// +// ============================================================================ + +#ifndef ACE_TESTS_FRAMEWORK_COMPONENT_DLL_H +#define ACE_TESTS_FRAMEWORK_COMPONENT_DLL_H + +#include "Framework_Component_DLL_Export.h" +#include "ace/Log_Msg.h" +#include "ace/Singleton.h" +#include "ace/Synch_Traits.h" + +/// This we will use to test the ACE_Framework_Repository +class Simple_Service +{ +public: + Simple_Service (void); + + virtual ~Simple_Service (void); + + //virtual const ACE_TCHAR *dll_name (void); + + virtual const ACE_TCHAR *name (void); + +}; + +// Adapter that contains the required dll_name() method and +// has the "right" name for our DLL. +template <class TYPE> +class FWCT_DLL_Singleton_Adapter_T : public TYPE +{ +public: + const ACE_TCHAR *dll_name (void) + { + FRAMEWORK_COMPONENT_DLL_TRACE ("FWCT_DLL_Singleton_Adapter_T::dll_name"); + return ACE_TEXT("Framework_Component_DLL"); + } +}; + +typedef ACE_DLL_Singleton_T < FWCT_DLL_Singleton_Adapter_T <Simple_Service>, + ACE_SYNCH_MUTEX > SS_SINGLETON; + +#endif /* ACE_TESTS_FRAMEWORK_COMPONENT_TEST_H */ diff --git a/ACE/tests/Framework_Component_DLL_Export.h b/ACE/tests/Framework_Component_DLL_Export.h new file mode 100644 index 00000000000..dbbd38fbf43 --- /dev/null +++ b/ACE/tests/Framework_Component_DLL_Export.h @@ -0,0 +1,54 @@ + +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. +// This file is generated automatically by generate_export_file.pl Framework_Component_DLL +// ------------------------------ +#ifndef FRAMEWORK_COMPONENT_DLL_EXPORT_H +#define FRAMEWORK_COMPONENT_DLL_EXPORT_H + +#include "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (FRAMEWORK_COMPONENT_DLL_HAS_DLL) +# define FRAMEWORK_COMPONENT_DLL_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && ! TEST_HAS_DLL */ + +#if !defined (FRAMEWORK_COMPONENT_DLL_HAS_DLL) +# define FRAMEWORK_COMPONENT_DLL_HAS_DLL 1 +#endif /* ! FRAMEWORK_COMPONENT_DLL_HAS_DLL */ + +#if defined (FRAMEWORK_COMPONENT_DLL_HAS_DLL) && (FRAMEWORK_COMPONENT_DLL_HAS_DLL == 1) +# if defined (FRAMEWORK_COMPONENT_DLL_BUILD_DLL) +# define Framework_Component_DLL_Export ACE_Proper_Export_Flag +# define FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# define FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_EXPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# else /* FRAMEWORK_COMPONENT_DLL_BUILD_DLL */ +# define Framework_Component_DLL_Export ACE_Proper_Import_Flag +# define FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# define FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_IMPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# endif /* FRAMEWORK_COMPONENT_DLL_BUILD_DLL */ +#else /* FRAMEWORK_COMPONENT_DLL_HAS_DLL == 1 */ +# define Framework_Component_DLL_Export +# define FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARATION(T) +# define FRAMEWORK_COMPONENT_DLL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +#endif /* FRAMEWORK_COMPONENT_DLL_HAS_DLL == 1 */ + +// Set FRAMEWORK_COMPONENT_DLL_NTRACE = 0 to turn on library specific tracing even if +// tracing is turned off for ACE. +#if !defined (FRAMEWORK_COMPONENT_DLL_NTRACE) +# if (ACE_NTRACE == 1) +# define FRAMEWORK_COMPONENT_DLL_NTRACE 1 +# else /* (ACE_NTRACE == 1) */ +# define FRAMEWORK_COMPONENT_DLL_NTRACE 0 +# endif /* (ACE_NTRACE == 1) */ +#endif /* !FRAMEWORK_COMPONENT_DLL_NTRACE */ + +#if (FRAMEWORK_COMPONENT_DLL_NTRACE == 1) +# define FRAMEWORK_COMPONENT_DLL_TRACE(X) +#else /* (FRAMEWORK_COMPONENT_DLL_NTRACE == 1) */ +# define FRAMEWORK_COMPONENT_DLL_TRACE(X) ACE_TRACE_IMPL(X) +#endif /* (FRAMEWORK_COMPONENT_DLL_NTRACE == 1) */ + +#endif /* FRAMEWORK_COMPONENT_DLL_EXPORT_H */ + +// End of auto generated file. diff --git a/ACE/tests/Framework_Component_Test.cpp b/ACE/tests/Framework_Component_Test.cpp new file mode 100644 index 00000000000..58c753c6200 --- /dev/null +++ b/ACE/tests/Framework_Component_Test.cpp @@ -0,0 +1,121 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program tests both the ACE_Framework_Compondent and ACE_Repository. +// Since Framework Components are singletons that can live in dlls loaded +// via the Service Configurator framework, this test uses that framework +// to load services from a dll that has a singleton based on ACE_DLL_Singleton. +// When the dll is finally ready to be unloaded, the singleton will be +// automatically cleaned up just-in-time. +// +// = AUTHOR +// Don Hinton <dhinton@ieee.org> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Service_Config.h" +#include "ace/ARGV.h" +#include "ace/DLL_Manager.h" + +ACE_RCSID(tests, Framework_Component_Test, "$Id$") + +// Define a few macros--because they're so much fun, and keep the +// code below a little cleaner... +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + +# define ADD_SERVICE(X) \ + ACE_TEXT ("dynamic Server_") ACE_TEXT(#X) \ + ACE_TEXT (" Service_Object * ") \ + ACE_TEXT ("Framework_Component_DLL:_make_Server_") ACE_TEXT(#X) \ + ACE_TEXT ("() ''") + +# define REMOVE_SERVICE(X) \ + ACE_TEXT ("remove Server_") ACE_TEXT(#X) + +#else /* ACE_USES_CLASSIC_SVC_CONF */ + +# define ADD_SERVICE(X) \ + ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Server_") ACE_TEXT(#X) ACE_TEXT("' ") \ + ACE_TEXT ("type='service_object'> <initializer init='_make_Server_") ACE_TEXT(#X) ACE_TEXT("' ") \ + ACE_TEXT ("path='Framework_Component_DLL' params=''/> </dynamic>") + +# define REMOVE_SERVICE(X) \ + ACE_TEXT ( "<?xml version='1.0'?> <remove id='Server_") ACE_TEXT(#X) \ + ACE_TEXT("'> </remove>") + +#endif /* ACE_USES_CLASSIC_SVC_CONF */ + +int +run_test (u_long unload_mask = 0) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Running test with mask = %s|%s\n"), + ACE_BIT_ENABLED(unload_mask, ACE_DLL_UNLOAD_POLICY_PER_DLL) == 0 + ? ACE_TEXT ("PER_PROCESS") : ACE_TEXT ("PER_DLL"), + ACE_BIT_ENABLED(unload_mask, ACE_DLL_UNLOAD_POLICY_LAZY) == 0 + ? ACE_TEXT ("EAGER") : ACE_TEXT ("LAZY"))); + + ACE_DLL_Manager::instance ()->unload_policy (unload_mask); + + // Now, let the ACE Service Configurator framework load our service from a + // dll, which contains a singleton. + ACE_Service_Config::open (ACE_TEXT ("Framework_Component_Test"), + ACE_DEFAULT_LOGGER_KEY, + 1, 1, 1); + + // Now add server 1. + ACE_Service_Config::process_directive (ADD_SERVICE(1)); + + // And unload the first one, could unload the dll. + ACE_Service_Config::process_directive (REMOVE_SERVICE(1)); + + // Now readd server 1. + ACE_Service_Config::process_directive (ADD_SERVICE(1)); + + // And load another service from the same library. + ACE_Service_Config::process_directive (ADD_SERVICE(2)); + + // Unload the first one again, should *not* unload the dll this time. + ACE_Service_Config::process_directive (REMOVE_SERVICE(1)); + + // And unload the second service. Since the ACE_DLL_Handle will no longer + // have any references, the ACE_DLL_Manager will apply it's current unloading + // strategy and either call ACE_OS::dlclose() immediately, schedule a timeout + // the the reactor to call dlclose() some time in the future, or keep the + // dll loaded until program termination. + ACE_Service_Config::process_directive (REMOVE_SERVICE(2)); + + // Force unloading so we'll be ready for the next test. + ACE_DLL_Manager::instance ()->unload_policy (ACE_DLL_UNLOAD_POLICY_DEFAULT); + + ACE_Service_Config::close (); + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT("Framework_Component_Test")); + + int retval = 0; + + // Use defaults, i.e., per process, eager unloading. + retval += run_test (ACE_DLL_UNLOAD_POLICY_DEFAULT); + + // Per process, lazy unloading + retval += run_test (ACE_DLL_UNLOAD_POLICY_LAZY); + + // Per dll, eager unloading + retval += run_test (ACE_DLL_UNLOAD_POLICY_PER_DLL); + + // Per dll, lazy unloading. + retval += run_test (ACE_DLL_UNLOAD_POLICY_PER_DLL | ACE_DLL_UNLOAD_POLICY_LAZY); + + ACE_END_TEST; + return retval == 0 ? 0 : -1; +} diff --git a/ACE/tests/Framework_Component_Test.h b/ACE/tests/Framework_Component_Test.h new file mode 100644 index 00000000000..d5127477812 --- /dev/null +++ b/ACE/tests/Framework_Component_Test.h @@ -0,0 +1,39 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Framework_Component_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Don Hinton <dhinton@ieee.org> +// +// ============================================================================ + +#ifndef ACE_TESTS_FRAMEWORK_COMPONENT_TEST_H +#define ACE_TESTS_FRAMEWORK_COMPONENT_TEST_H + +#include "ace/Log_Msg.h" + +/// This we will use to test the ACE_Framework_Repository +class Simple_Service +{ +public: + Simple_Service (void) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Simple_Service::Simple_Service\n"))); + } + virtual ~Simple_Service (void) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Simple_Service::~Simple_Service\n"))); + } +}; + +#endif /* ACE_TESTS_FRAMEWORK_COMPONENT_TEST_H */ diff --git a/ACE/tests/Future_Set_Test.cpp b/ACE/tests/Future_Set_Test.cpp new file mode 100644 index 00000000000..8f2eb9306ef --- /dev/null +++ b/ACE/tests/Future_Set_Test.cpp @@ -0,0 +1,578 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Future_Set_Test.cpp +// +// = DESCRIPTION +// This example tests the ACE Future Set and illustrates an +// implementation of the Active Object pattern, which is available +// at <http://www.cs.wustl.edu/~schmidt/Act-Obj.ps.gz>. The +// Active Object itself is very simple -- it determines if numbers +// are prime. +// +// = AUTHOR +// Andres Kruse <Andres.Kruse@cern.ch>, +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, +// Per Andersson <pera@ipso.se> and +// Johnny Tucker <jtucker@infoglide.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/ACE.h" +#include "ace/Task.h" +#include "ace/Message_Queue.h" +#include "ace/Future.h" +#include "ace/Future_Set.h" +#include "ace/Method_Request.h" +#include "ace/Activation_Queue.h" +#include "ace/Auto_Ptr.h" +#include "ace/Atomic_Op.h" +#include "ace/Null_Mutex.h" + +ACE_RCSID(tests, Future_Set_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +typedef ACE_Atomic_Op<ACE_Thread_Mutex, int> ATOMIC_INT; + +// A counter for the tasks.. +static ATOMIC_INT task_count (0); + +class Prime_Scheduler : public ACE_Task_Base +{ + // = TITLE + // Prime number scheduler for the Active Object. + // + // = DESCRIPTION + // This class also plays the role of the Proxy and the Servant + // in the Active Object pattern. Naturally, these roles could + // be split apart from the Prime_Scheduler. + + friend class Method_Request_work; + friend class Method_Request_name; + friend class Method_Request_end; +public: + // = Initialization and termination methods. + Prime_Scheduler (const ACE_TCHAR *, + Prime_Scheduler * = 0); + // Constructor. + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void *args = 0); + // Initializer. + + virtual int shutdown (void); + // Terminator. + //FUZZ: enable check_for_lack_ACE_OS + + virtual ~Prime_Scheduler (void); + // Destructor. + + // = These methods are part of the Active Object Proxy interface. + ACE_Future<u_long> work (u_long param, int count = 1); + ACE_Future<const ACE_TCHAR*> name (void); + void end (void); + +protected: + virtual int svc (void); + // Runs the Prime_Scheduler's event loop, which dequeues + // <Method_Requests> and dispatches them. + + // = These are the Servant methods that do the actual work. + u_long work_i (u_long, int); + const ACE_TCHAR *name_i (void); + +private: + // = These are the <Prime_Scheduler> implementation details. + ACE_TCHAR *name_; + ACE_Activation_Queue activation_queue_; + Prime_Scheduler *scheduler_; +}; + +class Method_Request_work : public ACE_Method_Request +{ + // = TITLE + // Reification of the <work> method. +public: + Method_Request_work (Prime_Scheduler *, + u_long, + int, + ACE_Future<u_long> &); + virtual ~Method_Request_work (void); + + virtual int call (void); + // This is the entry point into the Active Object method. + +private: + Prime_Scheduler *scheduler_; + + u_long param_; + // Parameter to the method that's used to determine if a number if + // prime. + + int count_; + // Unused. + + ACE_Future<u_long> future_result_; + // Store the result of the Future. +}; + +Method_Request_work::Method_Request_work (Prime_Scheduler *new_Prime_Scheduler, + u_long new_param, + int new_count, + ACE_Future<u_long> &new_result) + : scheduler_ (new_Prime_Scheduler), + param_ (new_param), + count_ (new_count), + future_result_ (new_result) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_work created\n"))); +} + +Method_Request_work::~Method_Request_work (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_work will be deleted.\n"))); +} + +int +Method_Request_work::call (void) +{ + // Dispatch the Servant's operation and store the result into the + // Future. + return this->future_result_.set (this->scheduler_->work_i + (this->param_, + this->count_)); +} + +class Method_Request_name : public ACE_Method_Request +{ + // = TITLE + // Reification of the <name> method. +public: + Method_Request_name (Prime_Scheduler *, + ACE_Future<const ACE_TCHAR*> &); + virtual ~Method_Request_name (void); + + virtual int call (void); + // This is the entry point into the Active Object method. + +private: + Prime_Scheduler *scheduler_; + ACE_Future<const ACE_TCHAR*> future_result_; +}; + +Method_Request_name::Method_Request_name (Prime_Scheduler *new_scheduler, + ACE_Future<const ACE_TCHAR*> &new_result) + : scheduler_ (new_scheduler), + future_result_ (new_result) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_name created\n"))); +} + +Method_Request_name::~Method_Request_name (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_name will be deleted.\n"))); +} + +int +Method_Request_name::call (void) +{ + // Dispatch the Servant's operation and store the result into the + // Future. + return future_result_.set (scheduler_->name_i ()); +} + +class Method_Request_end : public ACE_Method_Request +{ + // = TITLE + // Reification of the <end> method. +public: + Method_Request_end (Prime_Scheduler *new_Prime_Scheduler); + virtual ~Method_Request_end (void); + virtual int call (void); + +private: + Prime_Scheduler *scheduler_; +}; + +Method_Request_end::Method_Request_end (Prime_Scheduler *scheduler) + : scheduler_ (scheduler) +{ +} + +Method_Request_end::~Method_Request_end (void) +{ +} + +int +Method_Request_end::call (void) +{ + // Shut down the scheduler. + this->scheduler_->shutdown (); + return -1; +} + +// Constructor +Prime_Scheduler::Prime_Scheduler (const ACE_TCHAR *newname, + Prime_Scheduler *new_scheduler) + : scheduler_ (new_scheduler) +{ + ACE_NEW (this->name_, + ACE_TCHAR[ACE_OS::strlen (newname) + 1]); + ACE_OS::strcpy ((ACE_TCHAR *) this->name_, + newname); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s created\n"), + this->name_)); +} + +// Destructor + +Prime_Scheduler::~Prime_Scheduler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s will be destroyed\n"), + this->name_)); + delete [] this->name_; +} + +// open + +int +Prime_Scheduler::open (void *) +{ + task_count++; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s open\n"), + this->name_)); + // Become an Active Object. + return this->activate (THR_BOUND | THR_DETACHED); +} + +// close + +int +Prime_Scheduler::shutdown (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s shutdown\n"), + this->name_)); + task_count--; + return 0; +} + +// Service.. + +int +Prime_Scheduler::svc (void) +{ + for (;;) + { + // Dequeue the next method request (we use an auto pointer in + // case an exception is thrown in the <call>). + auto_ptr<ACE_Method_Request> mo (this->activation_queue_.dequeue ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) calling method request\n"))); + // Call it. + if (mo->call () == -1) + break; + // Destructor automatically deletes it. + } + + /* NOTREACHED */ + return 0; +} + +void +Prime_Scheduler::end (void) +{ + this->activation_queue_.enqueue (new Method_Request_end (this)); +} + +// Here's where the Work takes place. We compute if the parameter is +// a prime number. + +u_long +Prime_Scheduler::work_i (u_long param, + int count) +{ + ACE_UNUSED_ARG (count); + + return ACE::is_prime (param, 2, param / 2); +} + +const ACE_TCHAR * +Prime_Scheduler::name_i (void) +{ + return this->name_; +} + +ACE_Future<const ACE_TCHAR *> +Prime_Scheduler::name (void) +{ + if (this->scheduler_) + // Delegate to the Prime_Scheduler. + return this->scheduler_->name (); + else + { + ACE_Future<const ACE_TCHAR*> new_future; + + // @@ What happens if new fails here? + this->activation_queue_.enqueue + (new Method_Request_name (this, + new_future)); + return new_future; + } +} + +ACE_Future<u_long> +Prime_Scheduler::work (u_long newparam, + int newcount) +{ + if (this->scheduler_) { + return this->scheduler_->work (newparam, newcount); + } + else { + ACE_Future<u_long> new_future; + + this->activation_queue_.enqueue + (new Method_Request_work (this, + newparam, + newcount, + new_future)); + return new_future; + } +} + +// @@ These values should be set by the command line options! + +// Total number of loops. +static int n_loops = 100; + +typedef ACE_Future_Rep<u_long> *u_long_key; +typedef ACE_Future_Holder<u_long> *u_long_value; + +typedef ACE_Future_Rep<const ACE_TCHAR *> *char_star_key; +typedef ACE_Future_Holder<const ACE_TCHAR *> *char_star_value; + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Future_Set_Test")); + +#if defined (ACE_HAS_THREADS) + // @@ Should make these be <auto_ptr>s... + Prime_Scheduler *andres, *peter, *helmut, *matias; + + // Create active objects.. + ACE_NEW_RETURN (andres, + Prime_Scheduler (ACE_TEXT ("andres")), + -1); + int result = andres->open (); + ACE_ASSERT (result != -1); + ACE_NEW_RETURN (peter, + Prime_Scheduler (ACE_TEXT ("peter")), + -1); + result = peter->open (); + ACE_ASSERT (result != -1); + ACE_NEW_RETURN (helmut, + Prime_Scheduler (ACE_TEXT ("helmut")), + -1); + result = helmut->open (); + ACE_ASSERT (result != -1); + + // Matias passes all asynchronous method calls on to Andres... + ACE_NEW_RETURN (matias, + Prime_Scheduler (ACE_TEXT ("matias"), + andres), + -1); + result = matias->open (); + ACE_ASSERT (result != -1); + + ACE_Future<u_long> fresulta; + ACE_Future<u_long> fresultb; + ACE_Future<u_long> fresultc; + ACE_Future<u_long> fresultd; + ACE_Future<const ACE_TCHAR *> fname; + + ACE_Future_Set<u_long> fseta; + ACE_Future_Set<u_long> fsetb; + ACE_Future_Set<u_long> fsetc; + ACE_Future_Set<u_long> fsetd; + ACE_Future_Set<const ACE_TCHAR *> fsetname; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) initializing future sets with non-blocking call\n"))); + + for (int i = 0; i < n_loops; i++) + { + // Spawn off the methods, which run in a separate thread as + // active object invocations. + fresulta = andres->work (9013); + fresultb = peter->work (9013); + fresultc = helmut->work (9013); + fresultd = matias->work (9013); + fname = andres->name (); + + fsetname.insert (fname); + fname = peter->name (); + fsetname.insert (fname); + fname = helmut->name (); + + fseta.insert (fresulta); + fsetb.insert (fresultb); + fsetc.insert (fresultc); + fsetd.insert (fresultd); + fsetname.insert (fname); + } + + + // See if the result is available... + + if (!fseta.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set a is not empty.....\n"))); + + if (!fsetb.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set b is not empty.....\n"))); + + if (!fsetc.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set c is not empty.....\n"))); + + if (!fsetd.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set d is not empty.....\n"))); + + if (!fsetname.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set name is not empty.....\n"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) non-blocking calls done... now blocking...\n"))); + + // Save the result of fresulta. + + u_long resulta = 0; + u_long resultb = 0; + u_long resultc = 0; + u_long resultd = 0; + + u_int count = 0; + while (fseta.next_readable (fresulta)) + { + fresulta.get (resulta); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) result(%u) a %u\n"), + count, + (u_int) resulta)); + } + + count = 0; + while (fsetb.next_readable (fresultb)) + { + fresultb.get (resultb); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) result(%u) b %u\n"), + count, + (u_int) resultb)); + } + + count = 0; + while (fsetc.next_readable (fresultc)) + { + fresultc.get (resultc); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) result(%u) c %u\n"), + count, + (u_int) resultc)); + } + + count = 0; + while (fsetd.next_readable (fresultd)) + { + fresultd.get (resultd); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) result(%u) d %u\n"), + count, + (u_int) resultd)); + } + + const ACE_TCHAR *name = 0; + count = 0; + while (fsetname.next_readable (fname)) + { + fname.get (name); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) result(%u) name %s\n"), + count, + name)); + } + + if (fseta.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set a is empty.....\n"))); + + if (fsetb.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set b is empty.....\n"))); + + if (fsetc.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set c is empty.....\n"))); + + if (fsetd.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set d is empty.....\n"))); + + if (fsetname.is_empty ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. set name is empty.....\n"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) task_count %d\n"), + task_count.value () )); + + // Close things down. + andres->end (); + peter->end (); + helmut->end (); + matias->end (); + + ACE_Thread_Manager::instance ()->wait (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) task_count %d\n"), + task_count.value () )); + + delete andres; + delete peter; + delete helmut; + delete matias; + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Future_Test.cpp b/ACE/tests/Future_Test.cpp new file mode 100644 index 00000000000..0470d15c166 --- /dev/null +++ b/ACE/tests/Future_Test.cpp @@ -0,0 +1,609 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Future_Test.cpp +// +// = DESCRIPTION +// This example tests the ACE Future and illustrates an +// implementation of the Active Object pattern, which is described +// in the POSA2 book <www.cs.wustl.edu/~schmidt/POSA/> and in a +// paper at <www.cs.wustl.edu/~schmidt/PDF/Act-Obj.pdf>. The +// Active Object example here is very simple -- it determines if numbers +// are prime. +// +// = AUTHOR +// Andres Kruse <Andres.Kruse@cern.ch>, +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, +// and Per Andersson <pera@ipso.se> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/ACE.h" +#include "ace/Task.h" +#include "ace/Message_Queue.h" +#include "ace/Future.h" +#include "ace/Method_Request.h" +#include "ace/Activation_Queue.h" +#include "ace/Auto_Ptr.h" +#include "ace/Atomic_Op.h" + + +ACE_RCSID(tests, Future_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +typedef ACE_Atomic_Op<ACE_Thread_Mutex, int> ATOMIC_INT; + +// A counter for the tasks.. +static ATOMIC_INT task_count (0); + +// A counter for the futures.. +static ATOMIC_INT future_count (0); + +// A counter for the capsules.. +static ATOMIC_INT capsule_count (0); + +// A counter for the method requests... +static ATOMIC_INT method_request_count (0); + +class Prime_Scheduler : public ACE_Task_Base +{ + // = TITLE + // Prime number scheduler for the Active Object. + // + // = DESCRIPTION + // This class also plays the role of the Proxy and the Servant + // in the Active Object pattern. Naturally, these roles could + // be split apart from the Prime_Scheduler. + + friend class Method_Request_work; + friend class Method_Request_name; + friend class Method_Request_end; +public: + // = Initialization and termination methods. + Prime_Scheduler (const ACE_TCHAR *, + Prime_Scheduler * = 0); + // Constructor. + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void *args = 0); + // Initializer. + + virtual int shutdown (void); + // Terminator. + //FUZZ: enable check_for_lack_ACE_OS + + virtual ~Prime_Scheduler (void); + // Destructor. + + // = These methods are part of the Active Object Proxy interface. + ACE_Future<u_long> work (u_long param, int count = 1); + ACE_Future<const ACE_TCHAR*> name (void); + void end (void); + +protected: + virtual int svc (void); + // Runs the Prime_Scheduler's event loop, which dequeues + // <Method_Requests> and dispatches them. + + // = These are the Servant methods that do the actual work. + u_long work_i (u_long, int); + const ACE_TCHAR *name_i (void); + +private: + // = These are the <Prime_Scheduler> implementation details. + ACE_TCHAR *name_; + ACE_Activation_Queue activation_queue_; + Prime_Scheduler *scheduler_; +}; + +class Method_Request_work : public ACE_Method_Request +{ + // = TITLE + // Reification of the <work> method. +public: + Method_Request_work (Prime_Scheduler *, + u_long, + int, + ACE_Future<u_long> &); + virtual ~Method_Request_work (void); + + virtual int call (void); + // This is the entry point into the Active Object method. + +private: + Prime_Scheduler *scheduler_; + + u_long param_; + // Parameter to the method that's used to determine if a number if + // prime. + + int count_; + // Unused. + + ACE_Future<u_long> future_result_; + // Store the result of the Future. +}; + +Method_Request_work::Method_Request_work (Prime_Scheduler *new_Prime_Scheduler, + u_long new_param, + int new_count, + ACE_Future<u_long> &new_result) + : scheduler_ (new_Prime_Scheduler), + param_ (new_param), + count_ (new_count), + future_result_ (new_result) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_work created\n"))); +} + +Method_Request_work::~Method_Request_work (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_work will be deleted.\n"))); +} + +int +Method_Request_work::call (void) +{ + // Dispatch the Servant's operation and store the result into the + // Future. + return this->future_result_.set (this->scheduler_->work_i + (this->param_, + this->count_)); +} + +class Method_Request_name : public ACE_Method_Request +{ + // = TITLE + // Reification of the <name> method. +public: + Method_Request_name (Prime_Scheduler *, + ACE_Future<const ACE_TCHAR*> &); + virtual ~Method_Request_name (void); + + virtual int call (void); + // This is the entry point into the Active Object method. + +private: + Prime_Scheduler *scheduler_; + ACE_Future<const ACE_TCHAR*> future_result_; +}; + +Method_Request_name::Method_Request_name (Prime_Scheduler *new_scheduler, + ACE_Future<const ACE_TCHAR*> &new_result) + : scheduler_ (new_scheduler), + future_result_ (new_result) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_name created\n"))); +} + +Method_Request_name::~Method_Request_name (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_name will be deleted.\n"))); +} + +int +Method_Request_name::call (void) +{ + // Dispatch the Servant's operation and store the result into the + // Future. + return future_result_.set (scheduler_->name_i ()); +} + +class Method_Request_end : public ACE_Method_Request +{ + // = TITLE + // Reification of the <end> method. +public: + Method_Request_end (Prime_Scheduler *new_Prime_Scheduler); + virtual ~Method_Request_end (void); + virtual int call (void); + +private: + Prime_Scheduler *scheduler_; +}; + +Method_Request_end::Method_Request_end (Prime_Scheduler *scheduler) + : scheduler_ (scheduler) +{ +} + +Method_Request_end::~Method_Request_end (void) +{ +} + +int +Method_Request_end::call (void) +{ + // Shut down the scheduler. + this->scheduler_->shutdown (); + return -1; +} + +// Constructor +Prime_Scheduler::Prime_Scheduler (const ACE_TCHAR *newname, + Prime_Scheduler *new_scheduler) + : scheduler_ (new_scheduler) +{ + ACE_NEW (this->name_, + ACE_TCHAR[ACE_OS::strlen (newname) + 1]); + ACE_OS::strcpy ((ACE_TCHAR *) this->name_, + newname); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s created\n"), + this->name_)); +} + +// Destructor + +Prime_Scheduler::~Prime_Scheduler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s will be destroyed\n"), + this->name_)); + delete [] this->name_; +} + +// open + +int +Prime_Scheduler::open (void *) +{ + task_count++; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s open\n"), + this->name_)); + // Become an Active Object. + return this->activate (THR_BOUND | THR_DETACHED); +} + +// close + +int +Prime_Scheduler::shutdown (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Prime_Scheduler %s shutdown\n"), + this->name_)); + task_count--; + return 0; +} + +// Service.. + +int +Prime_Scheduler::svc (void) +{ + for (;;) + { + // Dequeue the next method request (we use an auto pointer in + // case an exception is thrown in the <call>). + auto_ptr<ACE_Method_Request> mo (this->activation_queue_.dequeue ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) calling method request\n"))); + // Call it. + if (mo->call () == -1) + break; + // Destructor automatically deletes it. + } + + /* NOTREACHED */ + return 0; +} + +void +Prime_Scheduler::end (void) +{ + this->activation_queue_.enqueue (new Method_Request_end (this)); +} + +// Here's where the Work takes place. We compute if the parameter is +// a prime number. + +u_long +Prime_Scheduler::work_i (u_long param, + int count) +{ + ACE_UNUSED_ARG (count); + + return ACE::is_prime (param, 2, param / 2); +} + +const ACE_TCHAR * +Prime_Scheduler::name_i (void) +{ + return this->name_; +} + +ACE_Future<const ACE_TCHAR *> +Prime_Scheduler::name (void) +{ + if (this->scheduler_) + // Delegate to the Prime_Scheduler. + return this->scheduler_->name (); + else + { + ACE_Future<const ACE_TCHAR*> new_future; + + // @@ What happens if new fails here? + this->activation_queue_.enqueue + (new Method_Request_name (this, + new_future)); + return new_future; + } +} + +ACE_Future<u_long> +Prime_Scheduler::work (u_long newparam, + int newcount) +{ + if (this->scheduler_) { + return this->scheduler_->work (newparam, newcount); + } + else { + ACE_Future<u_long> new_future; + + this->activation_queue_.enqueue + (new Method_Request_work (this, + newparam, + newcount, + new_future)); + return new_future; + } +} + +// @@ These values should be set by the command line options! + +// Total number of loops. +static int n_loops = 100; + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Future_Test")); + +#if defined (ACE_HAS_THREADS) + // @@ Should make these be <auto_ptr>s... + Prime_Scheduler *andres, *peter, *helmut, *matias; + + // Create active objects.. + ACE_NEW_RETURN (andres, + Prime_Scheduler (ACE_TEXT ("andres")), + -1); + int result = andres->open (); + ACE_ASSERT (result != -1); + ACE_NEW_RETURN (peter, + Prime_Scheduler (ACE_TEXT ("peter")), + -1); + result = peter->open (); + ACE_ASSERT (result != -1); + ACE_NEW_RETURN (helmut, + Prime_Scheduler (ACE_TEXT ("helmut")), + -1); + result = helmut->open (); + ACE_ASSERT (result != -1); + + // Matias passes all asynchronous method calls on to Andres... + ACE_NEW_RETURN (matias, + Prime_Scheduler (ACE_TEXT ("matias"), + andres), + -1); + result = matias->open (); + ACE_ASSERT (result != -1); + + for (int i = 0; i < n_loops; i++) + { + { + ACE_Future<u_long> fresulta; + ACE_Future<u_long> fresultb; + ACE_Future<u_long> fresultc; + ACE_Future<u_long> fresultd; + ACE_Future<u_long> fresulte; + ACE_Future<const ACE_TCHAR *> fname; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) going to do a non-blocking call\n"))); + + // Spawn off the methods, which run in a separate thread as + // active object invocations. + fresulta = andres->work (9013); + fresultb = peter->work (9013); + fresultc = helmut->work (9013); + fresultd = matias->work (9013); + fname = andres->name (); + + // See if the result is available... + if (fresulta.ready ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) wow.. work is ready.....\n"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) non-blocking call done... now blocking...\n"))); + + // Save the result of fresulta. + + fresulte = fresulta; + + if (i % 3 == 0) + { + // Every 3rd time... disconnect the futures... but + // "fresulte" should still contain the result... + fresulta.cancel (10ul); + fresultb.cancel (20ul); + fresultc.cancel (30ul); + fresultd.cancel (40ul); + } + + u_long resulta = 0, resultb = 0, resultc = 0, resultd = 0, resulte = 0; + + fresulta.get (resulta); + fresultb.get (resultb); + fresultc.get (resultc); + fresultd.get (resultd); + fresulte.get (resulte); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) result a %u\n") + ACE_TEXT ("(%t) result b %u\n") + ACE_TEXT ("(%t) result c %u\n") + ACE_TEXT ("(%t) result d %u\n") + ACE_TEXT ("(%t) result e %u\n"), + (u_int) resulta, + (u_int) resultb, + (u_int) resultc, + (u_int) resultd, + (u_int) resulte)); + + const ACE_TCHAR *name = 0; + fname.get (name); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) name %s\n"), + name)); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) task_count %d future_count %d ") + ACE_TEXT ("capsule_count %d method_request_count %d\n"), + task_count.value (), + future_count.value (), + capsule_count.value (), + method_request_count.value ())); + } + + // Close things down. + andres->end (); + peter->end (); + helmut->end (); + matias->end (); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) task_count %d future_count %d ") + ACE_TEXT ("capsule_count %d method_request_count %d\n"), + task_count.value (), + future_count.value (), + capsule_count.value (), + method_request_count.value ())); + { + // Check if set then get works, older versions of <ACE_Future> + // will lock forever (or until the timer expires), will use a + // small timer value to avoid blocking the process. + + ACE_Future<int> f1; + f1.set (100); + + // Note you need to use absolute time, not relative time. + ACE_Time_Value timeout (ACE_OS::gettimeofday () + ACE_Time_Value (10)); + int value = 0; + + if (f1.get (value, &timeout) == 0 + && value == 100) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Ace_Future<T>::Set followed by Ace_Future<T>::Get works.\n"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_Future<T>::Set followed by Ace_Future<T>::Get does ") + ACE_TEXT ("not work, broken Ace_Future<> implementation.\n"))); + } + + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Checking if Ace_Future<T>::operator= is implemented ") + ACE_TEXT ("incorrectly this might crash the program.\n"))); + ACE_Future<int> f1; + { + // To ensure that a rep object is created. + ACE_Future<int> f2 (f1); + } + // Now it is one ACE_Future<int> referencing the rep instance + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("0.\n"))); + + //Check that self assignment works. + f1 = f1; + + // Is there any repesentation left, and if so what is the ref + // count older ACE_Future<> implementations have deleted the rep + // instance at this moment + + // The stuff below might crash the process if the <operator=> + // implementation was bad. + int value = 0; + + ACE_Time_Value timeout (ACE_OS::gettimeofday () + ACE_Time_Value (10)); + + f1.set (100); + f1.get (value, &timeout); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("1.\n"))); + { + // Might delete the same data a couple of times. + ACE_Future<int> f2 (f1); + f1.set (100); + f1.get (value, &timeout); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("2.\n"))); + { + ACE_Future<int> f2 (f1); + f1.set (100); + f1.get (value, &timeout); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("3.\n"))); + { + ACE_Future<int> f2 (f1); + f1.set (100); + f1.get (value, &timeout); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("4.\n"))); + { + ACE_Future<int> f2 (f1); + f1.set (100); + f1.get (value, &timeout); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("5.\n"))); + { + ACE_Future<int> f2 (90); + f2.get (value, &timeout); + f1.get (value, &timeout); + } + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("No it did not crash the program.\n"))); + + delete andres; + delete peter; + delete helmut; + delete matias; + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Get_Opt_Test.cpp b/ACE/tests/Get_Opt_Test.cpp new file mode 100644 index 00000000000..f9d4442344b --- /dev/null +++ b/ACE/tests/Get_Opt_Test.cpp @@ -0,0 +1,346 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program tests both the short and long option support in +// <ACE_Get_Opt>, and demonstrates how to use it. +// +// = AUTHOR +// Don Hinton <dhinton@dresystems.com> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Get_Opt.h" +#include "ace/ARGV.h" +#include "ace/SString.h" +#include "ace/OS_NS_stdlib.h" +#include "tests/test_config.h" + +ACE_RCSID(tests, Get_Opt_Test, "$Id$") + +/* + * This is the heart of the test. It sets up the optstring, instantiates + * ACE_Get_Opt, add long options, processes them in a loop, and prints out + * the results to the log. + * + * It returns 0 for success and 1 for error so we can keep track of the + * total error count. + */ + +static const ACE_TString empty (ACE_TEXT ("")); + +static int +parse_args (int test_number, + int ordering, + const ACE_TCHAR *test_args, + int skip_argv = 1, + int report_errors = 1, + const ACE_TString &opt_prefix = empty) +{ + ACE_TString test; + ACE_TString optstring (opt_prefix); + + // Test the skip_argv for the first test only. + if (skip_argv > 0) + { + test = ACE_TEXT ("Test_"); + ACE_TCHAR s[20]; + test += ACE_OS::itoa (test_number, s, 10); + test += ACE_TEXT (" "); + } + + test += test_args; + optstring += ACE_TEXT ("fr:o::sW;"); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" TEST %d *****************************************") + ACE_TEXT ("*******************\n"), + test_number)); + ACE_DEBUG ((LM_INFO, " Command line: \"%s\"\n", test.c_str ())); + + ACE_ARGV args (test.c_str ()); + + ACE_Get_Opt get_opt (args.argc (), + args.argv (), + optstring.c_str (), + skip_argv, + report_errors, + ordering); + + // Now add the default long args. + if (get_opt.long_option (ACE_TEXT ("flag"), + 'f', + ACE_Get_Opt::NO_ARG) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option 'f' \n")), 1); + + if (get_opt.long_option (ACE_TEXT ("requires_arg"), + 'r', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option 'r' \n")), 1); + + if (get_opt.long_option (ACE_TEXT ("optional_arg"), + 'o', + ACE_Get_Opt::ARG_OPTIONAL) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option 'o' \n")), 1); + + if (get_opt.long_option (ACE_TEXT ("long_option"), + 'l', + ACE_Get_Opt::ARG_OPTIONAL) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option 'l' \n")), 1); + + if (get_opt.long_option (ACE_TEXT ("long_only"), + -11, + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option ") + ACE_TEXT ("\"long_only\" \n")), 1); + + if (get_opt.long_option (ACE_TEXT ("long_no_arg")) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option ") + ACE_TEXT ("\"long_no_arg\" \n")), 1); + + // This is the special case of providing a non-alpha numeric corresponding + // short option. This lets you use the corresponding short option in a + // switch statement, even thought a meaningful short options isn't available + // (afterall, there are only so many alpha numeric characters available). + if (get_opt.long_option (ACE_TEXT ("non_alpha-num_short"), + -10, + ACE_Get_Opt::ARG_OPTIONAL) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add long option ") + ACE_TEXT ("\"non_alpha_short\" \n")), 1); + + // We print out the optstring here because adding long_options that + // have corresponding short options that aren't yet present, are added. + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" optstring: \"%s\" skip_argv: %d\n"), + get_opt.optstring (), skip_argv)); + + // Now, let's parse it... + int c = 0; + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 0: + // Long Option. + if (!get_opt.long_option ()) + ACE_ERROR_RETURN ((LM_ERROR, " Long option doesn't exist.\n"), 1); + + ACE_DEBUG ((LM_INFO, " Found long option \"%s\" %s %s\n", + get_opt.long_option (), + get_opt.opt_arg () ? ACE_TEXT ("with argument:") + : ACE_TEXT (""), + get_opt.opt_arg () ? get_opt.opt_arg () + : ACE_TEXT (""))); + break; + case 'f': + // This flag was added in both the optstring in the ctor and with + // long_option(). + case 's': + // This one is only short and has no long option. + ACE_DEBUG ((LM_INFO, " Found option flag '%s'\n", + get_opt.last_option ())); + break; + case 'r': + // This one has a required argument, we wouldn't be here if the + // arg were missing. Note that we call get_opt.opt_arg () to return + // the argument, but we could have used get_opt.opt_arg () since + // opt_arg () is defined as "opt_arg ()". + ACE_DEBUG ((LM_INFO, + " Found option '%s' with required argument \"%s\"\n", + get_opt.last_option (), get_opt.opt_arg ())); + break; + case 'o': + // This one has an optional argument. + case 'l': + // This short options was set automatically added to optstring. + ACE_DEBUG ((LM_INFO, + " Found option '%s' with optional argument \"%s\"\n", + get_opt.last_option (), + get_opt.opt_arg () ? get_opt.opt_arg () + : ACE_TEXT ("default"))); + break; + case 1: + // Non-option when in RETURN_IN_ORDER mode. + ACE_DEBUG ((LM_INFO, + " Found a non-option argument \"%s\" before finding " + "\"--\" (must be in RETURN_IN_ORDER mode).\n", + get_opt.opt_arg ())); + break; + case -10: + // we found the short option that isn't alpha numeric. + ACE_DEBUG ((LM_INFO, + " Found option '%s' with optional argument \"%s\"\n", + get_opt.last_option (), + get_opt.opt_arg () ? get_opt.opt_arg () + : ACE_TEXT ("default"))); + break; + case -11: + // we found the short option that isn't alpha numeric. + ACE_DEBUG ((LM_INFO, + " Found option '%s' with argument \"%s\"\n", + get_opt.last_option (), get_opt.opt_arg ())); + break; + case ':': + // This means an option requiring an argument didn't have one. + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" Option '%c' (%s) requires an argument ") + ACE_TEXT ("but none was supplied\n"), + get_opt.opt_opt (), get_opt.last_option ())); + break; + case '?': + // An unrecognized option. + default: + // This is an error, perhaps you could handle them, but let's + // just log it and keep going + ACE_DEBUG ((LM_INFO, + " Found an unknown option (%s) we couldn't handle.\n", + get_opt.last_option ())); + } + } + + // Print out the rest of the arguments left in the command line (if any). + int index = 0; + for (index = get_opt.opt_ind (); index < args.argc (); index++) + ACE_DEBUG ((LM_INFO, " Found non-option argument \"%s\"\n", + args.argv ()[index])); + + // Now print them all so we can examine the permuted cmd line. + for (index = 0; index < args.argc (); index++) + ACE_DEBUG ((LM_INFO, " argv[%u] \"%s\"\n", + index, args.argv ()[index])); + return 0; +} + +/* + * Add new tests cases here. We increment the test number and pass the + * type of ordering we want so that each test can be tested with multiple + * ordering values in order to demostrate the difference. + * + * The return value is cumulative, and serves as a failure count that is + * returned at the end of all the tests. + */ +static int +run_test (int& test_number, int ordering) +{ + int retval = 0; + + ACE_DEBUG ((LM_INFO, + " ########## Running Tests with ordering = %C ##########\n", + ordering == 1 ? "REQUIRE_ORDER" : + ordering == 2 ? "PERMUTE_ARGS" : + "RETURN_IN_ORDER")); + + // Basic test, but don't use the program name and don't skip any args. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-f -rreq-arg -oopt-arg -lopt-arg --long_only=lo_arg -s arg1 arg2"), + 0); + + // Same, but combining short args that don't take args + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fsrreq-arg -oopt-arg -lopt-arg --long_only=lo_arg arg1 arg2")); + + // Now we use defaults for options with optional args. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fsrreq-arg -o -l --long_only=lo_arg arg1 arg2")); + + // Let's mix up the options and non-options an see what happens. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fs arg1 -rreq-arg -o arg2 -l --long_only=lo_arg")); + + // Now we turn off options parsing explicitely by passing "--" in the middle. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fs -rreq-arg -- arg1 -o arg2 -l --long_only=lo_arg")); + + // Let's try the same thing but mix up the options and non-options. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fs arg1 arg2 -rreq-arg -- arg3 -o")); + + // One more time but with "-W". + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fs arg1 arg2 -rreq-arg -W long_option=opt-arg -- arg3 -o")); + + // Let's pass some long options. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("--flag -s --requires_arg=req-arg --optional_arg --long_only lo_arg arg1 arg2")); + + // And long options without the '='. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("--flag -s --requires_arg req-arg --optional_arg --long_only=lo_arg arg1 arg2")); + + // Pass "-W" to cause the next argument to be read as a long option. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fso -W requires_arg=req-arg -Woptional_arg -l arg1 arg2")); + + // This is the special case of a non-alpha numeric short option. + retval += parse_args + (test_number++, ordering, + ACE_TEXT ("-fso --non_alpha-num_short=xxx arg1 arg2")); + + // Now, let's test some error conditions (we turn off report_errors since they are + // intentional, we don't want to break the test script) + int report_errors = 0; + + // Bad short option. + retval += parse_args (test_number++, ordering, ACE_TEXT ("-X"), 1, report_errors); + + // Short option with missing required arg. + ACE_TString report_missing (ACE_TEXT (":")); + retval += parse_args (test_number++, ordering, ACE_TEXT ("-r"), 1, report_errors, report_missing); + + // Short option with missing required arg with trailing "--". + // This reads "--" as the arg, but should it? + retval += parse_args (test_number++, ordering, ACE_TEXT ("-r --"), 1, report_errors); + + // Long option with missing required arg. + retval += parse_args (test_number++, ordering, ACE_TEXT ("--long_only"), 1, report_errors); + + // Long option that doesn't take an arg has one. + retval += parse_args (test_number++, ordering, ACE_TEXT ("--long_no_arg=bad_arg"), 1, report_errors); + + // Unknown long option. + retval += parse_args (test_number++, ordering, ACE_TEXT ("--unknown"), 1, report_errors); + + // Ambigous long option. + retval += parse_args (test_number++, ordering, ACE_TEXT ("--long"), 1, report_errors); + + return retval; +} + +int +run_main (int, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Get_Opt_Test")); + ACE_UNUSED_ARG (argv); + int retval = 0; + int test_number = 0; + + // Run the tests for each type of ordering. + retval = run_test (test_number, ACE_Get_Opt::PERMUTE_ARGS); + retval += run_test (test_number, ACE_Get_Opt::REQUIRE_ORDER); + retval += run_test (test_number, ACE_Get_Opt::RETURN_IN_ORDER); + + ACE_END_TEST; + return retval; +} diff --git a/ACE/tests/Handle_Set_Test.cpp b/ACE/tests/Handle_Set_Test.cpp new file mode 100644 index 00000000000..529d269296b --- /dev/null +++ b/ACE/tests/Handle_Set_Test.cpp @@ -0,0 +1,228 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Handle_Set_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of ACE_Handle_Set to maintain a +// set of handles. No command line arguments are needed to run +// the test. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/Profile_Timer.h" +#include "ace/Handle_Set.h" +#include "ace/Containers.h" + +ACE_RCSID(tests, Handle_Set_Test, "$Id$") + +static void +test_duplicates (size_t count) +{ + size_t duplicates = 0; + size_t sets = 0; + size_t clears = 0; + + ACE_Handle_Set handle_set; + + ACE_OS::srand ((u_int) ACE_OS::time (0L)); + + for (size_t i = 0; i < count; i++) + { + size_t handle = + static_cast<size_t> (ACE_OS::rand () % ACE_Handle_Set::MAXSIZE); + + if (ACE_ODD (handle)) + { + if (handle_set.is_set ((ACE_HANDLE) handle)) + duplicates++; + + handle_set.set_bit ((ACE_HANDLE) handle); + sets++; + } + else + { + if (handle_set.is_set ((ACE_HANDLE) handle)) + duplicates--; + + handle_set.clr_bit ((ACE_HANDLE) handle); + clears++; + } + } + + ACE_ASSERT (count == sets + clears); + ACE_ASSERT (handle_set.num_set () + duplicates == sets); +} + +// This is the vector of handles to test. These numbers are chosen to +// test for boundaries conditions. Note that if +// <ACE_DEFAULT_SELECT_REACTOR_SIZE> is less than any of these +// <HANDLE> values, the logic in <test_boundaries> will simply ignore +// these values. +#if defined (ACE_WIN64) + // The casts below are legit... +# pragma warning(push) +# pragma warning(disable : 4312) +#endif /* ACE_WIN64 */ +static ACE_HANDLE handle_vector[] = +{ + (ACE_HANDLE) 0, + (ACE_HANDLE) 1, + (ACE_HANDLE) 31, + (ACE_HANDLE) 32, + (ACE_HANDLE) 63, + (ACE_HANDLE) 64, + (ACE_HANDLE) 65, + (ACE_HANDLE) 127, + (ACE_HANDLE) 128, + (ACE_HANDLE) 129, + (ACE_HANDLE) 254, + (ACE_HANDLE) 255, + (ACE_HANDLE) (ACE_DEFAULT_SELECT_REACTOR_SIZE - 1), + (ACE_HANDLE) ACE_DEFAULT_SELECT_REACTOR_SIZE, + ACE_INVALID_HANDLE +}; +#if defined (ACE_WIN64) +# pragma warning(pop) +#endif /* ACE_WIN64 */ + +static void +test_boundaries (void) +{ + ACE_Handle_Set handle_set; + ACE_Unbounded_Set<ACE_HANDLE> set; + ACE_HANDLE handle; + + // First test an empty set. + + for (ACE_Handle_Set_Iterator i1 (handle_set); + (handle = i1 ()) != ACE_INVALID_HANDLE; + ) + { + ACE_ASSERT (0 == + ACE_TEXT ("this shouldn't get called since ") + ACE_TEXT ("the set is empty!\n")); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_DEFAULT_SELECT_REACTOR_SIZE %d\n"), + ACE_DEFAULT_SELECT_REACTOR_SIZE)); + + // Insert the vector of <ACE_HANDLE>s into the set. + +#if defined (ACE_WIN64) + // The casts below are legit... +# pragma warning(push) +# pragma warning(disable : 4312) +#endif /* ACE_WIN64 */ + for (int i = 0; + handle_vector[i] != ACE_INVALID_HANDLE; + i++) + { + if (handle_vector[i] < (ACE_HANDLE) ACE_DEFAULT_SELECT_REACTOR_SIZE) + { + handle_set.set_bit (handle_vector[i]); + set.insert (handle_vector[i]); + } + } +#if defined (ACE_WIN64) +# pragma warning(pop) +#endif /* ACE_WIN64 */ + + int count = 0; + + for (ACE_Handle_Set_Iterator i2 (handle_set); + (handle = i2 ()) != ACE_INVALID_HANDLE; + ) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("obtained handle %d\n"), + handle)); + int done = set.remove (handle); + ACE_ASSERT (done == 0); + count++; + } + + ACE_ASSERT (count == handle_set.num_set ()); +} + +static void +test_performance (size_t max_handles, + size_t max_iterations) +{ + ACE_Handle_Set handle_set; + size_t i; + + for (i = 0; i < max_handles; i++) + handle_set.set_bit ((ACE_HANDLE) i); + + ACE_Profile_Timer timer; + size_t count = 0; + + timer.start (); + + for (i = 0; i < max_iterations; i++) + { + ACE_Handle_Set_Iterator iter (handle_set); + + // Only iterate up to <handle_set.max_set ()>. + while (iter () != ACE_INVALID_HANDLE) + count++; + } + + timer.stop (); + + ACE_ASSERT (count == max_handles * max_iterations); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, ") + ACE_TEXT ("system time = %f secs\n"), + et.real_time, + et.user_time, + et.system_time)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per each of the %d calls = %f usecs\n"), + count, + et.real_time / double (count) * 1000000)); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Handle_Set_Test")); + + int count = argc > 1 + ? ACE_OS::atoi (argv[1]) + : ACE_Handle_Set::MAXSIZE; + size_t max_handles = + argc > 2 + ? ACE_OS::atoi (argv[2]) + : ACE_Handle_Set::MAXSIZE; + size_t max_iterations = + argc > 3 + ? ACE_OS::atoi (argv[3]) + : ACE_MAX_ITERATIONS; + + test_duplicates (count); + test_boundaries (); + test_performance (max_handles, + max_iterations); + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Hash_Map_Bucket_Iterator_Test.cpp b/ACE/tests/Hash_Map_Bucket_Iterator_Test.cpp new file mode 100644 index 00000000000..ab1cae561df --- /dev/null +++ b/ACE/tests/Hash_Map_Bucket_Iterator_Test.cpp @@ -0,0 +1,122 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Hash_Map_Bucket_Iterator_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the <Hash_Map_Bucket_Iterator>. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/Hash_Map_Manager.h" +#include "ace/Synch_Traits.h" +#include "ace/Null_Mutex.h" +#include "ace/Get_Opt.h" +#include "ace/Functor.h" + +ACE_RCSID(tests, Hash_Map_Bucket_Iterator_Test, "$Id$") + +static ACE_UINT32 iterations = ACE_MAX_ITERATIONS * 10; +static ACE_UINT32 table_size = ACE_MAX_ITERATIONS; + +int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("t:i:")); + + int cc; + + while ((cc = get_opt ()) != -1) + switch (cc) + { + case 't': + table_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case '?': + case 'h': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s ") + ACE_TEXT ("[-i (iterations)] ") + ACE_TEXT ("[-t (table size)] "), + ACE_TEXT ("Hash_Map_Bucket_Iterator_Test"))); + return -1; + } + + return 0; +} + +typedef ACE_Hash_Map_Manager_Ex<ACE_UINT32, + ACE_UINT32, + ACE_Hash<ACE_UINT32>, + ACE_Equal_To<ACE_UINT32>, + ACE_SYNCH_NULL_MUTEX> HASH_MAP; + +typedef ACE_Hash_Map_Bucket_Iterator<ACE_UINT32, + ACE_UINT32, + ACE_Hash<ACE_UINT32>, + ACE_Equal_To<ACE_UINT32>, + ACE_SYNCH_NULL_MUTEX> HASH_MAP_BUCKET_ITERATOR; + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + // Validate options. + int result = parse_args (argc, argv); + if (result != 0) + return result; + + // Start the test only if options are valid. + ACE_START_TEST (ACE_TEXT ("Hash_Map_Bucket_Iterator_Test")); + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + ACE_UINT32 i = 0; + HASH_MAP map (table_size); + + for (i = 0; i < iterations; ++i) + { + int result = map.bind (i, i); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + } + + for (i = 0; i < table_size; ++i) + { + HASH_MAP_BUCKET_ITERATOR iterator (map, + i); + + HASH_MAP_BUCKET_ITERATOR end (map, + i, + 1); + + for (; + iterator != end; + ++iterator) + { + ACE_DEBUG ((LM_DEBUG, "%d ", (*iterator).int_id_)); + ACE_UINT32 key = (*iterator).ext_id_; + ACE_ASSERT (((key - i) % table_size) == 0); + } + + ACE_DEBUG ((LM_DEBUG, "\n")); + } + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/Hash_Map_Manager_Test.cpp b/ACE/tests/Hash_Map_Manager_Test.cpp new file mode 100644 index 00000000000..e960e127f8c --- /dev/null +++ b/ACE/tests/Hash_Map_Manager_Test.cpp @@ -0,0 +1,343 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Hash_Map_Manager_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of <ACE_Hash_Map_Manager> to +// maintain a hash table using strings. In addition, it also +// illustrates how the <ACE_Static_Allocator> works in +// conjunction with the <ACE_Hash_Map_Manager>. +// +// = AUTHOR +// James Hu <jxh@cs.wustl.edu> and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "STL_algorithm_Test_T.h" +#include "ace/Hash_Map_Manager.h" +#include "ace/Malloc_T.h" +#include "ace/Null_Mutex.h" + +ACE_RCSID(tests, Hash_Map_Manager_Test, "$Id$") + +static const size_t STRING_TABLE_ENTRIES = 3 * 2; +static const size_t MAX_HASH = 6; + +typedef ACE_Hash_Map_Entry<const ACE_TCHAR *, + const ACE_TCHAR *> HASH_STRING_ENTRY; + +// @@ The following requires too much internal implementation +// information about the <ACE_Hash_Map_Manager>. We need to figure +// out how to simplify this. +static const size_t STRING_TABLE_SIZE = + sizeof (HASH_STRING_ENTRY) * (STRING_TABLE_ENTRIES + MAX_HASH); + +static ACE_Static_Allocator<STRING_TABLE_SIZE> ace_test_allocator; + +typedef ACE_Hash_Map_Manager_Ex<const ACE_TCHAR *, + const ACE_TCHAR *, + ACE_Hash<const ACE_TCHAR *>, + ACE_Equal_To<const ACE_TCHAR *>, + ACE_Null_Mutex> HASH_STRING_MAP; + +typedef ACE_Hash_Map_Iterator_Ex<const ACE_TCHAR *, + const ACE_TCHAR *, + ACE_Hash<const ACE_TCHAR *>, + ACE_Equal_To<const ACE_TCHAR *>, + ACE_Null_Mutex> HASH_STRING_ITER; + +typedef ACE_Hash_Map_Const_Iterator_Ex<const ACE_TCHAR *, + const ACE_TCHAR *, + ACE_Hash<const ACE_TCHAR *>, + ACE_Equal_To<const ACE_TCHAR *>, + ACE_Null_Mutex> HASH_STRING_CONST_ITER; + +typedef ACE_Hash_Map_Reverse_Iterator_Ex<const ACE_TCHAR *, + const ACE_TCHAR *, + ACE_Hash<const ACE_TCHAR *>, + ACE_Equal_To<const ACE_TCHAR *>, + ACE_Null_Mutex> HASH_STRING_REVERSE_ITER; + +struct String_Table +{ + const ACE_TCHAR *key_; + const ACE_TCHAR *value_; +}; + +static String_Table string_table[] = +{ + { + ACE_TEXT ("hello"), + ACE_TEXT ("guten Tag") + }, + { + ACE_TEXT ("goodbye"), + ACE_TEXT ("auf wiedersehen") + }, + { + ACE_TEXT ("funny"), + ACE_TEXT ("lustig") + }, + { + 0, + 0 + } +}; + +static +int test_two_allocators () +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing both allocators\n"))); + int status = 0; + + // Number of entries in string_table above: + size_t chunks = 3; + size_t chunk_size = sizeof (HASH_STRING_MAP::ENTRY); + + // Allocators: + ACE_Dynamic_Cached_Allocator<ACE_Null_Mutex> table_alloc (1 , chunk_size * chunks); + ACE_Dynamic_Cached_Allocator<ACE_Null_Mutex> table_alloc_small (1, chunk_size * chunks - 1); + ACE_Cached_Allocator<HASH_STRING_MAP::ENTRY, ACE_Null_Mutex> entry_alloc (chunks); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing hash map manager with %d elements...\n"), chunks)); + + HASH_STRING_MAP hash; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Opening hash map manager with ") + ACE_TEXT ("insufficient table allocator, should fail...\n"))); + ACE_OS::last_error (0); + status = hash.open (chunks, &table_alloc_small, &entry_alloc); + if (status < 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("OK, failed: %d (%s)\n"), + status, ACE_OS::strerror (ACE_OS::last_error ()))); + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("*** Something is wrong...\n")), -1); + + status = hash.close (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Table allocator depth: %d.\n"), + table_alloc.pool_depth ())); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Entry allocator depth: %d.\n"), + entry_alloc.pool_depth ())); + + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("Opening hash map manager again, should succeed...\n"))); + ACE_OS::last_error (0); + status = hash.open (chunks, &table_alloc, &entry_alloc); + if (status < 0) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("*** Something is wrong: %d (%s)\n"), + status, ACE_OS::strerror (ACE_OS::last_error ())), -1); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("OK.\n"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Table allocator depth: %d\n"), + table_alloc.pool_depth ())); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Entry allocator depth: %d.\n"), + entry_alloc.pool_depth ())); + + for (size_t i = 0; i < chunks; i++) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("- Binding ('%s', '%s'), should succeed...\n"), + string_table[i].key_ , string_table[i].value_)); + ACE_OS::last_error (0); + status = hash.bind (string_table[i].key_, string_table[i].value_); + if (status < 0) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("*** Something is wrong: %d (%s)\n"), + status, ACE_OS::strerror (ACE_OS::last_error ())), -1); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("OK, entry allocator depth: %d\n"), + entry_alloc.pool_depth ())); + } + + const ACE_TCHAR *key = ACE_TEXT ("key"); + const ACE_TCHAR *val = ACE_TEXT ("value"); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("- Binding ('%s', '%s'), should fail...\n"), + key, val)); + ACE_OS::last_error (0); + status = hash.bind (key, val); + if (status < 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("OK, failed (%s).\n"), + ACE_OS::strerror (ACE_OS::last_error ()))); + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("*** Something is wrong: %d (%s)\n"), + status, ACE_OS::strerror (ACE_OS::last_error ())), -1); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Closing hash map.\n"))); + status = hash.close (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Table allocator depth: %d.\n"), + table_alloc.pool_depth ())); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Entry allocator depth: %d.\n"), + entry_alloc.pool_depth ())); + + return 0; +} + +static int +run_test (void) +{ + ace_test_allocator.dump (); + + HASH_STRING_MAP hash (MAX_HASH, &ace_test_allocator); + + size_t i; + + // Check the <bind> operation. + for (i = 0; string_table[i].key_ != 0; i++) + if (hash.bind (string_table[i].key_, + string_table[i].value_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p failed for %s \n"), + ACE_TEXT ("bind"), + string_table[i].key_), -1); + + const ACE_TCHAR *entry; + + // Check the <find> operation. + for (i = 0; string_table[i].key_ != 0; i++) + if (hash.find (string_table[i].key_, + entry) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("`%s' found `%s'\n"), + string_table[i].key_, + entry)); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("`%s' not found\n"), + string_table[i].key_), + -1); + + // Check the <trybind> operation. + { + const ACE_TCHAR *pc = string_table[1].value_; + if (hash.trybind (string_table[0].key_, + pc) != 1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("::trybind missed existing entry.")), + -1); + else if (pc != string_table[0].value_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("::trybind doesn't return existing value.")), + -1); + } + + // Let's test the iterator while we are at it. + { + HASH_STRING_ENTRY *entry = 0; + size_t i = 0; + + for (HASH_STRING_ITER hash_iter (hash); + hash_iter.next (entry) != 0; + hash_iter.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("iterating (%d): [%s, %s]\n"), + i, + entry->ext_id_, + entry->int_id_)); + i++; + } + } + + // And now test the const iterator + { + HASH_STRING_ENTRY *entry = 0; + size_t i = 0; + + for (HASH_STRING_CONST_ITER hash_iter (hash); + hash_iter.next (entry) != 0; + hash_iter.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("const iterating (%d): [%s, %s]\n"), + i, + entry->ext_id_, + entry->int_id_)); + i++; + } + } + + hash.unbind (string_table[2].key_, entry); + + // Check the <find> operation again. + for (i = 0; string_table[i].key_ != 0; i++) + if (hash.find (string_table[i].key_, + entry) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("`%s' found `%s'\n"), + string_table[i].key_, + entry)); + else if (i != 2) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("`%s' not found\n"), + string_table[i].key_), + -1); + + // Let's test the iterator backwards. + { + HASH_STRING_ENTRY *entry = 0; + size_t i = 0; + + for (HASH_STRING_REVERSE_ITER hash_iter (hash); + hash_iter.next (entry) != 0; + hash_iter.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("iterating (%d): [%s, %s]\n"), + i, + entry->ext_id_, + entry->int_id_)); + i++; + } + } + + // Remove all the entries. + if (hash.unbind_all () != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unbind_all failed\n")), + -1); + + // Redo the <bind> operations. + for (i = 0; string_table[i].key_ != 0; i++) + if (hash.bind (string_table[i].key_, + string_table[i].value_) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p failed for %s \n"), + ACE_TEXT ("bind"), + string_table[i].key_), -1); + + ace_test_allocator.dump (); + + test_two_allocators(); + + // Run the STL algorithm test on the hash map. + const HASH_STRING_MAP & chash = hash; + + test_STL_algorithm (hash); + test_STL_algorithm (chash); + + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Hash_Map_Manager_Test")); + + run_test (); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Hash_Multi_Map_Manager_Test.cpp b/ACE/tests/Hash_Multi_Map_Manager_Test.cpp new file mode 100644 index 00000000000..2e883a71152 --- /dev/null +++ b/ACE/tests/Hash_Multi_Map_Manager_Test.cpp @@ -0,0 +1,384 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Hash_Multi_Map_Manager_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of <ACE_Hash_Multi_Map_Manager> to +// maintain a hash table using strings. +// +// = AUTHOR +// Shanshan Jiang <shanshan.jiang@vanderbilt.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Hash_Multi_Map_Manager_T.h" +#include "ace/Null_Mutex.h" + +ACE_RCSID(tests, Hash_Multi_Map_Manager_Test, "$Id$") + +typedef ACE_Hash_Multi_Map_Manager<const ACE_TCHAR *, + const ACE_TCHAR *, + ACE_Hash<const ACE_TCHAR *>, + ACE_Equal_To<const ACE_TCHAR *>, + ACE_Null_Mutex> HASH_STRING_MAP; + +typedef HASH_STRING_MAP::ENTRY HASH_STRING_ENTRY; +typedef HASH_STRING_ENTRY::VALUE_SET HASH_STRING_VALUE_SET; +typedef HASH_STRING_ENTRY::VALUE_SET_ITERATOR HASH_STRING_VALUE_SET_ITERATOR; +typedef HASH_STRING_MAP::ITERATOR HASH_STRING_ITERATOR; +typedef HASH_STRING_MAP::CONST_ITERATOR HASH_STRING_CONST_ITERATOR; +typedef HASH_STRING_MAP::REVERSE_ITERATOR HASH_STRING_REVERSE_ITERATOR; + +static const ACE_TCHAR *color_sorts[] = {ACE_TEXT ("warm"), ACE_TEXT ("cold"), ACE_TEXT ("neutral"), 0}; +static const ACE_TCHAR *warm_colors[] = {ACE_TEXT ("red"), ACE_TEXT ("yellow"), ACE_TEXT ("orange"), 0}; +static const ACE_TCHAR *cold_colors[] = {ACE_TEXT ("blue"), ACE_TEXT ("cyan"), 0}; +static const ACE_TCHAR *neutral_colors[] = {ACE_TEXT ("green"), ACE_TEXT ("purple"), 0}; + +static int +run_test (void) +{ + HASH_STRING_MAP colors; + + size_t i; + + // Check the <bind> operation. + for (i = 0; warm_colors[i] != 0; i++) + { + if (colors.bind (color_sorts[0], + warm_colors[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s in %s\n"), + warm_colors[i], + color_sorts[0]), + -1); + } + + HASH_STRING_ENTRY *entry = 0; + for (i = 0; cold_colors[i] != 0; i++) + { + if (colors.bind (color_sorts[1], + cold_colors[i], + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s in %s\n"), + cold_colors[i], + color_sorts[1]), + -1); + } + + HASH_STRING_VALUE_SET value_set; + entry = 0; + for (i = 0; neutral_colors[i] != 0; i++) + value_set.insert (neutral_colors[i]); + if (colors.bind (color_sorts[2], + value_set, + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + // Check the <trybind> operation. + value_set.reset (); + if (colors.trybind (color_sorts[2], + value_set) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + entry = 0; + if (colors.trybind (color_sorts[2], + value_set, + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + entry = 0; + if (colors.trybind (color_sorts[2], + value_set, + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + // Check the <rebind> operation. + value_set.reset (); + value_set.insert (neutral_colors[0]); + if (colors.rebind (color_sorts[2], + value_set) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + value_set.insert (neutral_colors[1]); + entry = 0; + if (colors.rebind (color_sorts[2], + value_set, + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + value_set.insert (neutral_colors[0]); + HASH_STRING_VALUE_SET old_value_set; + if (colors.rebind (color_sorts[2], + value_set, + old_value_set) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + value_set.insert (neutral_colors[1]); + old_value_set.reset (); + entry = 0; + if (colors.rebind (color_sorts[2], + value_set, + old_value_set, + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + value_set.insert (neutral_colors[0]); + const ACE_TCHAR *old_key; + old_value_set.reset (); + if (colors.rebind (color_sorts[2], + value_set, + old_key, + old_value_set) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + value_set.reset (); + for (i = 0; neutral_colors[i] != 0; i++) + value_set.insert (neutral_colors[i]); + old_key = 0; + old_value_set.reset (); + entry = 0; + if (colors.rebind (color_sorts[2], + value_set, + old_key, + old_value_set, + entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s\n"), + color_sorts[2]), + -1); + + // Check the <find> operation. + for (i = 0; color_sorts[i] != 0; i++) + if (colors.find (color_sorts[i]) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s found\n"), + color_sorts[i])); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s not found\n"), + color_sorts[i]), + -1); + + entry = 0; + for (i = 0; color_sorts[i] != 0; i++) + if (colors.find (color_sorts[i], + entry) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s found\n"), + color_sorts[i])); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s not found\n"), + color_sorts[i]), + -1); + + value_set.reset (); + for (i = 0; color_sorts[i] != 0; i++) + if (colors.find (color_sorts[i], + value_set) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s found\n"), + color_sorts[i])); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s not found\n"), + color_sorts[i]), + -1); + + for (i = 0; warm_colors[i] != 0; i++) + if (colors.find (color_sorts[0], + warm_colors[i]) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s color %s found\n"), + color_sorts[0], + warm_colors[i])); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s color %s not found\n"), + color_sorts[0], + warm_colors[i]), + -1); + + // Check the iterator. + i = 0; + entry = 0; + for (HASH_STRING_ITERATOR hash_iter (colors); + hash_iter.next (entry) != 0; + hash_iter.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("iterating (%d): %s:\n"), + i, + entry->ext_id_)); + i++; + + for (HASH_STRING_VALUE_SET_ITERATOR iter (entry->int_id_set_); + iter != entry->int_id_set_.end (); + iter++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s \n"), + *iter)); + } + } + + // Check the unbind operation. + if (colors.unbind (color_sorts[0]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unbind failed for %s\n"), + color_sorts[1]), + -1); + for (i = 0; warm_colors[i] != 0; i++) + { + if (colors.bind (color_sorts[0], + warm_colors[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s in %s\n"), + warm_colors[i], + color_sorts[0]), + -1); + } + + value_set.reset (); + if (colors.unbind (color_sorts[0], value_set) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unbind failed for %s\n"), + color_sorts[1]), + -1); + for (i = 0; warm_colors[i] != 0; i++) + { + if (colors.bind (color_sorts[0], + warm_colors[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("bind failed for %s in %s\n"), + warm_colors[i], + color_sorts[0]), + -1); + } + + for (i = 0; warm_colors[i] != 0; i++) + { + if (colors.unbind (color_sorts[0], + warm_colors[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unbind failed for %s in %s\n"), + warm_colors[i], + color_sorts[0]), + -1); + } + + // Check the reverse iterator. + i = 0; + entry = 0; + for (HASH_STRING_REVERSE_ITERATOR hash_iter (colors); + hash_iter.next (entry) != 0; + hash_iter.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("reverse iterating (%d): %s\n"), + i, + entry->ext_id_)); + i++; + + for (HASH_STRING_VALUE_SET_ITERATOR iter (entry->int_id_set_); + iter != entry->int_id_set_.end (); + iter++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s \n"), + *iter)); + } + } + + entry = 0; + colors.find (color_sorts[1], entry); + if (colors.unbind (entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unbind failed for %s\n"), + color_sorts[1]), + -1); + + // Check the const iterator. + i = 0; + entry = 0; + for (HASH_STRING_CONST_ITERATOR hash_iter (colors); + hash_iter.next (entry) != 0; + hash_iter.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("const iterating (%d): %s\n"), + i, + entry->ext_id_)); + i++; + + for (HASH_STRING_VALUE_SET_ITERATOR iter (entry->int_id_set_); + iter != entry->int_id_set_.end (); + iter++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s \n"), + *iter)); + } + } + + // Check the unbind_all operation. + if (colors.unbind_all () != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unbind_all failed\n")), + -1); + + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Hash_Multi_Map_Manager_Test")); + + run_test (); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/High_Res_Timer_Test.cpp b/ACE/tests/High_Res_Timer_Test.cpp new file mode 100644 index 00000000000..5bfcb7ca573 --- /dev/null +++ b/ACE/tests/High_Res_Timer_Test.cpp @@ -0,0 +1,187 @@ +// $Id$ +//============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// High_Res_Timer_Test.cpp +// +// = DESCRIPTION +// Simple test of ACE_High_Res_Timer. +// +// = AUTHOR +// David L. Levine <levine@cs.wustl.edu> +// +//============================================================================ + +#include "test_config.h" +#include "ace/Log_Msg.h" +#include "ace/High_Res_Timer.h" +#include "ace/Sched_Params.h" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, High_Res_Timer_Test, "$Id$") + +static +u_int +check (const time_t interval, const time_t measured) +{ + time_t const threshold = 25 /* percent */; + + time_t const difference = + interval > measured ? interval - measured : measured - interval; + + time_t const percentage_difference = difference * 100 / interval; + + if (percentage_difference < threshold) + return 0; + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("The measured time of %Q differs from ") + ACE_TEXT ("the interval of %Q by %Q percent.\n"), + measured, + interval, + percentage_difference), + 1); +} + +// Does a sanity check of the microseconds vs nanoseconds. They're supposed +// to represent the same interval. +static u_int +check_micro_nano (ACE_hrtime_t microinterval, ACE_hrtime_t nanointerval) +{ + ACE_hrtime_t const threshold = 8 /* promille */; + + microinterval *= 1000u; + ACE_hrtime_t const difference = (microinterval > nanointerval + ? (microinterval - nanointerval) + : (nanointerval - microinterval)); + if (nanointerval == 0) + nanointerval = 1; // Prevent divide-by-zero + ACE_hrtime_t const promille_difference = + difference * 1000 / nanointerval; + + if ((promille_difference < threshold) || (difference < 1500)) + return 0; + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("The microseconds * 1000 of %Q ") + ACE_TEXT ("differs from the nanoseconds of %Q ") + ACE_TEXT (" by %Q promille\n"), + microinterval, + nanointerval, + promille_difference), + 1); +} + +static +ACE_Time_Value +time_interval (const ACE_Time_Value &interval, + ACE_hrtime_t& nanoseconds, + ACE_hrtime_t& microseconds) +{ + ACE_High_Res_Timer timer; + + timer.start (); + ACE_OS::sleep (interval); + timer.stop (); + + ACE_Time_Value measured; + timer.elapsed_time (measured); + timer.elapsed_time (nanoseconds); + timer.elapsed_microseconds (microseconds); + return measured; +} + + +static u_int const intervals[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 4000000}; /*usec*/ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("High_Res_Timer_Test")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("The ACE_High_Res_Timer scale factor is %u ") + ACE_TEXT ("1/microsecond\n"), + ACE_High_Res_Timer::global_scale_factor ())); + + u_int errors = 0; + + u_int iterations = 1; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("i:")); + for (int c; (c = getopt ()) != -1; ) + { + //FUZZ: enable check_for_lack_ACE_OS + + switch (c) + { + case 'i': + iterations = ACE_OS::atoi (getopt.opt_arg ()); + break; + } + } + + // We don't check for errors if the interval is shorter than this + // value because the OS has a finite resolution anyway. + static u_int const TIMER_RESOLUTION = 10000; + + for (u_int i = 0; i < sizeof intervals / sizeof (u_int); ++i) + { + for (u_int j = 0; j < iterations; ++j) + { + const ACE_Time_Value interval (0, intervals[i]); + ACE_hrtime_t nanoseconds; + ACE_hrtime_t microseconds; + const ACE_Time_Value measured = time_interval (interval, + nanoseconds, + microseconds); + time_t const interval_usec = + interval.sec () * ACE_ONE_SECOND_IN_USECS + interval.usec (); + time_t const measured_usec = + measured.sec () * ACE_ONE_SECOND_IN_USECS + measured.usec (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("interval: %: usec, measured: %: usec %s\n"), + interval_usec, + measured_usec, + (intervals[i] <= TIMER_RESOLUTION + ? ACE_TEXT (" (interval and measured may differ)") + : ACE_TEXT ("")))); + + if (intervals[i] > TIMER_RESOLUTION) + { + errors += check (interval.sec () * ACE_ONE_SECOND_IN_USECS + + interval.usec (), + measured.sec () * ACE_ONE_SECOND_IN_USECS + + measured.usec ()); + // Don't check for error for intervals below 10 msec. + } + // Check the ACE_Timer_Value-calculated microseconds against + // the ACE_High_Res_Timer-calculated nanoseconds. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_Time_Value usec: %:, ACE_HR nsec: %Q\n"), + measured_usec, + nanoseconds)); + // This gives problems -> should be fixed + errors += check_micro_nano (measured.sec () * ACE_ONE_SECOND_IN_USECS + + measured.usec (), + nanoseconds); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_High_Res_Timer usec: %Q, nsec: %Q\n"), + microseconds, + nanoseconds)); + // Now check the ACE_High_Res_Timer-calculated values against + // each other. + errors += check_micro_nano (microseconds, nanoseconds); + } + } + + ACE_END_TEST; + return errors == 0 ? 0 : 1; +} diff --git a/ACE/tests/INET_Addr_Test.cpp b/ACE/tests/INET_Addr_Test.cpp new file mode 100644 index 00000000000..4f1e27bad96 --- /dev/null +++ b/ACE/tests/INET_Addr_Test.cpp @@ -0,0 +1,257 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// INET_Addr_Test.cpp +// +// = DESCRIPTION +// Performs several tests on the ACE_INET_Addr class. It creates several +// IPv4 and IPv6 addresses and checks that the address formed by the +// class is valid. +// +// = AUTHOR +// John Aughey (jha@aughey.com) +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/INET_Addr.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_arpa_inet.h" + +// Make sure that ACE_Addr::addr_type_ is the same +// as the family of the inet_addr_. +int check_type_consistency (const ACE_INET_Addr &addr) +{ + int family = -1; + + if (addr.get_type () == AF_INET) + { + struct sockaddr_in *sa4 = (struct sockaddr_in *)addr.get_addr(); + family = sa4->sin_family; + } +#if defined (ACE_HAS_IPV6) + else if (addr.get_type () == AF_INET6) + { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)addr.get_addr(); + family = sa6->sin6_family; + } +#endif + + if (addr.get_type () != family) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Inconsistency between ACE_SOCK::addr_type_ (%d) ") + ACE_TEXT ("and the sockaddr family (%d)\n"), + addr.get_type (), + family)); + return 1; + } + return 0; +} + +struct Address { + const char* name; + bool loopback; +}; + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("INET_Addr_Test")); + + int status = 0; // Innocent until proven guilty + + const char *ipv4_addresses[] = + { + "127.0.0.1", "138.38.180.251", "64.219.54.121", "192.0.0.1", "10.0.0.1", 0 + }; + + ACE_INET_Addr addr; + status |= check_type_consistency (addr); + char hostaddr[1024]; + + for (int i=0; ipv4_addresses[i] != 0; i++) + { + struct in_addr addrv4; + ACE_UINT32 addr32; + + ACE_OS::inet_pton (AF_INET, ipv4_addresses[i], &addrv4); + + ACE_OS::memcpy (&addr32, &addrv4, sizeof (addr32)); + + addr.set (80, ipv4_addresses[i]); + status |= check_type_consistency (addr); + + /* + ** Now check to make sure get_ip_address matches and get_host_addr + ** matches. + */ + if (addr.get_ip_address () != ACE_HTONL (addr32)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: %s failed get_ip_address() check\n") + ACE_TEXT ("0x%x != 0x%x\n"), + ipv4_addresses[i], + addr.get_ip_address (), + addr32)); + status = 1; + } + + if (addr.get_host_addr () != 0 && + ACE_OS::strcmp (addr.get_host_addr(), ipv4_addresses[i]) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed get_host_addr() check\n") + ACE_TEXT ("%s != %s\n"), + ipv4_addresses[i], + addr.get_host_addr (), + ipv4_addresses[i])); + status = 1; + } + + // Now we check the operation of get_host_addr(char*,int) + const char* haddr = addr.get_host_addr (&hostaddr[0], sizeof(hostaddr)); + if (haddr != 0 && + ACE_OS::strcmp (&hostaddr[0], haddr) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed get_host_addr(char* buf,int) check\n") + ACE_TEXT ("buf ['%s'] != return value ['%s']\n"), + ipv4_addresses[i], + &hostaddr[0], + haddr)); + status = 1; + } + if (ACE_OS::strcmp (&hostaddr[0], ipv4_addresses[i]) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed get_host_addr(char*,int) check\n") + ACE_TEXT ("buf ['%s'] != expected value ['%s']\n"), + ipv4_addresses[i], + &hostaddr[0], + ipv4_addresses[i])); + status = 1; + } + + // Clear out the address by setting it to 1 and check + addr.set (0, ACE_UINT32 (1), 1); + status |= check_type_consistency (addr); + if (addr.get_ip_address () != 1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Failed to set address to 1\n"))); + status = 1; + } + + // Now set the address using a 32 bit number and check that we get + // the right string out of get_host_addr(). + addr.set (80, addr32, 0); // addr32 is already in network byte order + status |= check_type_consistency(addr); + + if (addr.get_host_addr () != 0 && + ACE_OS::strcmp (addr.get_host_addr (), ipv4_addresses[i]) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed second get_host_addr() check\n") + ACE_TEXT ("return value ['%s'] != expected value ['%s']\n"), + ipv4_addresses[i], + addr.get_host_addr (), + ipv4_addresses[i])); + status = 1; + } + + // Test for ACE_INET_Addr::set_addr(). + struct sockaddr_in sa4; + sa4.sin_family = AF_INET; + sa4.sin_addr = addrv4; + sa4.sin_port = ACE_HTONS (8080); + + addr.set (0, ACE_UINT32 (1), 1); + addr.set_addr (&sa4, sizeof(sa4)); + status |= check_type_consistency (addr); + + if (addr.get_port_number () != 8080) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_INET_Addr::set_addr() ") + ACE_TEXT ("failed to update port number.\n"))); + status = 1; + } + + if (addr.get_ip_address () != ACE_HTONL (addr32)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_INET_Addr::set_addr() ") + ACE_TEXT ("failed to update address.\n"))); + status = 1; + } + + } + +#if defined (ACE_HAS_IPV6) + if (ACE::ipv6_enabled ()) + { + const char *ipv6_addresses[] = { + "1080::8:800:200c:417a", // unicast address + "ff01::101", // multicast address + "::1", // loopback address + "::", // unspecified addresses + 0 + }; + + for (int i=0; ipv6_addresses[i] != 0; i++) + { + ACE_INET_Addr addr (80, ipv6_addresses[i]); + status |= check_type_consistency (addr); + + if (0 != ACE_OS::strcmp (addr.get_host_addr (), ipv6_addresses[i])) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("IPv6 get_host_addr failed: %s != %s\n"), + addr.get_host_addr (), + ipv6_addresses[i])); + status = 1; + } + } + } + +#endif + + struct Address loopback_addresses[] = + { {"127.0.0.1", true}, {"127.1.2.3", true} + , {"127.0.0.0", true}, {"127.255.255.255", true} + , {"126.255.255.255", false}, {"128.0.0.0", false}, {0, true} + }; + + for (int i=0; loopback_addresses[i].name != 0; i++) + { + struct in_addr addrv4; + ACE_UINT32 addr32 = 0; + + ACE_OS::inet_pton (AF_INET, loopback_addresses[i].name, &addrv4); + + ACE_OS::memcpy (&addr32, &addrv4, sizeof (addr32)); + + addr.set (80, loopback_addresses[i].name); + + if (addr.is_loopback() != loopback_addresses[i].loopback) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_INET_Addr::is_loopback() ") + ACE_TEXT ("failed to distinguish loopback address. %s\n") + , loopback_addresses[i].name)); + status = 1; + } + } + + ACE_END_TEST; + + return status; +} diff --git a/ACE/tests/INET_Addr_Test_IPV6.cpp b/ACE/tests/INET_Addr_Test_IPV6.cpp new file mode 100644 index 00000000000..134119ae7b9 --- /dev/null +++ b/ACE/tests/INET_Addr_Test_IPV6.cpp @@ -0,0 +1,167 @@ +// $Id$ + +// ============================================================================ +/** + * @file INET_Addr_Test_IPV6.cpp + * + * @brief Additional tests on the ACE_INET_Addr class above and beyond + * those found in INET_Addr_Test. These primarily focus on additional + * functionality and bug fixes to ACE_INET_Addr. + * + * @author John Aughey <jha@aughey.com> + * Brian Buesker <bbuesker@qualcomm.com> + */ +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/INET_Addr.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_arpa_inet.h" + +#define LINK_LOCAL_ADDR ACE_TEXT ("fe80::") +#define INTERFACE ("eth0") + +// Make sure that ACE_Addr::addr_type_ is the same +// as the family of the inet_addr_. +int check_type_consistency (const ACE_INET_Addr &addr) +{ + int family = -1; + + if (addr.get_type () == AF_INET) + { + struct sockaddr_in *sa4 = (struct sockaddr_in *)addr.get_addr(); + family = sa4->sin_family; + } +#if defined (ACE_HAS_IPV6) + else if (addr.get_type () == AF_INET6) + { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)addr.get_addr(); + family = sa6->sin6_family; + } +#endif + + if (addr.get_type () != family) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Inconsistency between ACE_SOCK::addr_type_ (%d) ") + ACE_TEXT ("and the sockaddr family (%d)\n"), + addr.get_type (), + family)); + return 1; + } + return 0; +} + + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("INET_Addr_Test_IPV6")); + + int status = 0; // Innocent until proven guilty + +#if defined (ACE_HAS_IPV6) + if (ACE::ipv6_enabled ()) + { + ACE_INET_Addr addr ("::"); + + // this should switch it back to an IPv4 address + addr.set (80); + + if (AF_INET != addr.get_type()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("set failed: Address type %d != AF_INET\n"), + addr.get_type())); + status = 1; + } + + // this should get mapped to an IPv6 address + addr.set (80, ACE_UINT32 (INADDR_ANY), 1, 1); + + if (AF_INET6 != addr.get_type()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("set failed: Address type %d != AF_INET6\n"), + addr.get_type())); + status = 1; + } + + // Test for ACE_INET_Addr::set_addr(). + struct in_addr addrv4; + + ACE_OS::inet_pton (AF_INET, "127.0.0.1", &addrv4); + + struct sockaddr_in sa4; + sa4.sin_family = AF_INET; + sa4.sin_addr = addrv4; + sa4.sin_port = ACE_HTONS (8080); + + addr.set (0, ACE_UINT32 (1), 1); + + // test to make sure this doesn't get mapped to an IPv6 address + addr.set_addr (&sa4, sizeof(sa4), 0); + + if (addr.get_type() != AF_INET) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT + ("set_addr failed: Address type %d != AF_INET\n"), + addr.get_type())); + status = 1; + } + + // now test to make sure it does get mapped to an IPv6 address + addr.set_addr (&sa4, sizeof(sa4), 1); + + if (addr.get_type() != AF_INET6) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT + ("set_addr failed: Address type %d != AF_INET6\n"), + addr.get_type())); + status = 1; + } + + // test to make sure that the type gets set correctly when set is + // called with another ACE_INET_Addr + addr.set (0, ACE_UINT32 (1), 1); + + ACE_INET_Addr addrIPv6 ((u_short) 0, ACE_IPV6_LOCALHOST); + + addr.set (addrIPv6); + + status |= check_type_consistency (addr); + +#if defined (__linux__) + // test a link local address to make sure the set_interface method works + ACE_INET_Addr link_local_addr (80, LINK_LOCAL_ADDR); + if (0 != ACE_OS::strcmp (ACE_TEXT_CHAR_TO_TCHAR(link_local_addr.get_host_addr ()), + LINK_LOCAL_ADDR)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("IPv6 get_host_addr failed: %s != %s\n"), + link_local_addr.get_host_addr (), + LINK_LOCAL_ADDR)); + status = 1; + } + + if (-1 == link_local_addr.set_interface (INTERFACE)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("IPv6 set_interface failed\n"))); + status = 1; + } +#endif /* __linux__ */ + } + +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + + return status; +} diff --git a/ACE/tests/INTEGRITY.ld b/ACE/tests/INTEGRITY.ld new file mode 100644 index 00000000000..7b77515f096 --- /dev/null +++ b/ACE/tests/INTEGRITY.ld @@ -0,0 +1,31 @@ +# default link map for INTEGRITY +# text (readonly) and data (read/write) areas must be page-aligned + +-sec +{ +# text/readonly segment + .picbase 0x10000 : + .text : + .syscall : + .intercall : + .interfunc : + .secinfo : + .rodata : + .sdata2 : + .fixaddr : + .fixtype : +# .textchecksum MUST be the last section of the text/readonly segment + .textchecksum : + +# data/readwrite segment + .pidbase align(0x4000) : + .data : + .sdabase : + .sdata : +# .datachecksum MUST be the last section of the data portion of the +# data/read/write segment (i.e., it must come before the BSS sections). + .datachecksum : + .sbss : + .bss : + .heap align(16) pad(0x800000) : +} diff --git a/ACE/tests/IOStream_Test.cpp b/ACE/tests/IOStream_Test.cpp new file mode 100644 index 00000000000..ffb758975f4 --- /dev/null +++ b/ACE/tests/IOStream_Test.cpp @@ -0,0 +1,494 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// IOStream_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the IOStream class that illustrates +// how to use iostream operations on almost arbitrary I/O classes. +// +// = AUTHOR +// James CE Johnson <jcej@lads.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/IOStream.h" +#include "ace/OS_NS_sys_wait.h" + +ACE_RCSID (tests, IOStream_Test, "$Id$") + +#if !defined (ACE_LACKS_ACE_IOSTREAM) +# include "ace/OS_NS_unistd.h" +# include "ace/OS_NS_ctype.h" // Needed for isspace() function + +typedef ACE_IOStream<ACE_SOCK_Stream> ACE_SOCK_IOStream; + +/* The biggest drawback to an iostream is that it generally + eats up whitespace when performing a get (>>) operation. + + That may be good if you're reading non-textual data but + if you're trying to read a stream of words with embedded + whitespace, it isn't going to be pleasant. + + If you've been blessed with the GNU String class, I've + already provided a derived class, QuotedString, that + makes dealing with strings very easy. + + If you're stuck with an array of characters then you + will probably need somthing like I have below. + + On the other hand, one of the biggest advantages to an + iostream is that it eats up whitespace :-) + + If you put (<<) your non-textual data to the iostream + with any number of whitespace between the data then + you can easily get (>>) the data from the iostream + without having to worry about delimeters and such. + + The main thing to keep in mind when using an iostream + between peers is that you MUST keep the data "fields" + in sync. That is, if the "putter" puts an int followed + by a float followed by a double, you must make sure + that the "getter" will be attempting to get an int + then a float then a double. + */ + +// Since I can't rely on GNU's String class being everywhere (yet), +// here's a simple class that will work with quoted strings. Use at +// your own risk! It is very incomplete! + +class qchar +{ +public: + qchar (void) { c_ = '\0'; } + + qchar (char c) : c_ (c) { }; + + operator char () const { return c_; } + + qchar operator= (char c) { return c_ = c; } + + bool operator== (char c) { return c_ == c; } + + friend ACE_SOCK_IOStream &operator>> (ACE_SOCK_IOStream & stream, qchar * buf); + friend ACE_SOCK_IOStream &operator<< (ACE_SOCK_IOStream & stream, qchar * buf); + friend ostream &operator<< (ostream & stream, qchar * buf); + +private: + char c_; +}; + +// This is taken almost directly from the QuotedString object that has +// been derived from GNU's String class. The advantage to using +// QuotedString is that it is MUCH more robust than qchar will every +// be. + +ACE_SOCK_IOStream & +operator>> (ACE_SOCK_IOStream & stream, qchar *buf) +{ + char c; + + *buf = '\0'; // Initialize the string + + stream.get (c); + + if (!stream) // eat space up to the first char + return stream; + + // if we don't have a quote, append until we see space + if (c != '"') + for (*buf++ = c; + (void *) stream.get (c) && !ACE_OS::ace_isspace (c); + *buf++ = c) + continue; + else + for (; (void *) stream.get (c) && c != '"'; *buf++ = c) + if (c == '\\') + { + stream.get (c); + if (c != '"') + *buf++ = '\\'; + } + + *buf = '\0'; + + return stream; +} + +ACE_SOCK_IOStream & +operator<< (ACE_SOCK_IOStream &stream, qchar *buf) +{ + stream.put ('"'); + + while (*buf) + { + if (*buf == '"') + stream.put ('\\'); + + stream.put ((char) *buf++); + } + + stream.put ('"'); + + return stream; +} + +ostream & +operator<< (ostream &stream, qchar *buf) +{ + while (*buf) + stream.put ((char) *buf++); + + return stream; +} + +// Our client thread will initiate the test by sending some data to +// the server. + +static void * +client (void *arg = 0) +{ + ACE_UNUSED_ARG (arg); + + // We don't _need_ to dynamically allocate the ACE_SOCK_IOStream. + // But if we don't, it doesn't get destroyed on some platforms, + // e.g., g++ 2.7.2.1 and Sun C++ 4.2 on Solaris 2.5.1. (It does work + // on Linux, so the code seems fine.) If we manage the storage + // ourselves, we _will_ destroy it at the end of this function. + ACE_SOCK_IOStream server; + + ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg; + ACE_INET_Addr addr (remote_addr->get_port_number (), + ACE_DEFAULT_SERVER_HOST); + ACE_SOCK_Connector connector; + + if (connector.connect (server, addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("Failed to connect to server thread")), + 0); + + // Send a string to the server which it can interpret as a qchar[] + const char *str = "\"This is a test string.\""; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%P|%t) Client Sending: (%s)\n"), + ACE_TEXT_CHAR_TO_TCHAR (str))); + server << str << endl; + + // Allow the server to get the string and echo it to the user. (The + // iostream doesn't need this, but humans do :) + ACE_OS::sleep (2); + + // Send another string but this time the server will read it as a + // char[]. Notice how the server's output doesn't include all of + // the spaces sent by the client. + + str = "\"THIS IS A TEST STRING.\""; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Client Sending: (%s)\n"), + str)); + server << str << endl; + + // Again, give the server time to display the happenings to the + // user. + ACE_OS::sleep (2); + + // Read from the server an int, float, long, float double. The + // iostream will pull them out by using the whitespace provided by + // the server. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Client Receiving\n"))); + + ACE_Time_Value timeout (2); + ACE_Time_Value *timeoutp = &timeout; + + server >> timeoutp; + + int i; + float f1, f2; + long l; + double d; + + while (! (server >> i)) + { + int eof = server.eof (); + if (eof) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Unrecoverable stream error/eof\n"))); + break; + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Recoverable stream error/timed out)\n"))); + server.clear (0); + } + } + + server >> f1; + server >> l; + server >> f2; + server >> d; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Client Received: int %d float %f long %d float %f double %f\n"), + i, + f1, + (int) l, + f2, + d)); + + // Check for proper received values. + ACE_ASSERT (i == 1 && (f1 >= 0.123420 && f1 <= 0.123422) + && l == 666555444 && (f2 >= 23.44 && f2 <= 23.46) + && (d >= -47.1e+9 && d <= -45.9e+9)); + // Reset the precision to limit ourselves to two significant digits. + server.precision (2); + + // Now, make a little change & send 'em back. + i *= -1; server << i << " "; + f1 *= -1.0; server << f1 << " "; + l *= -1; server << l << " "; + f2 *= -1.0; server << f2 << " "; + d *= -1; server << d << " "; + server << endl; + + // Shut down the test. + server.close (); + + return 0; +} + +// Test the server's ability to receive data from the client and then +// begin a two-way conversation. + +static void * +server (void *arg = 0) +{ + // We don't _need_ to dynamically allocate the ACE_SOCK_IOStream. + // But if we don't, it doesn't get destroyed on some platforms, + // e.g., g++ 2.7.2.1 and Sun C++ 4.2 on Solaris 2.5.1. (It does work + // on Linux, so the code seems fine.) If we manage the storage + // ourselves, we _will_ destroy it at the end of this function. + ACE_SOCK_IOStream client_handler; + + ACE_INET_Addr server_addr; + ACE_SOCK_Acceptor *acceptor = + reinterpret_cast<ACE_SOCK_Acceptor *> (arg); + + if (acceptor->get_local_addr (server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr")), + 0); + +#if defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (client), + (void *) &server_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("spawing client thread")), + 0); +#endif /* ACE_HAS_THREADS */ + + if (acceptor->accept (client_handler) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P|%t) Failed to accept new client_handler")), + 0); + + // Read a qbuf[] from the client. Notice that all of the client's + // whitespace is preserved. + qchar qbuf[BUFSIZ]; + ACE_OS::memset (qbuf, 0, sizeof qbuf); + client_handler >> qbuf; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Received: (\"%s\")\n"), + ACE_TEXT_CHAR_TO_TCHAR ((char *) qbuf))); + + // Give the client time to announce the next test to the user. + ACE_OS::sleep (2); + + // Now we try to use a char[] to get a string from the client. + // Compared to the method above, this is quite messy. Notice also + // that whitespace is lost. + +#if defined (ACE_HAS_STRING_CLASS) && defined (ACE_HAS_STANDARD_CPP_LIBRARY) + ACE_IOStream_String buf; + ACE_DEBUG ((LM_DEBUG, + " (%P|%t) Server Received: (")); + + while (client_handler && + (buf.length () == 0 || buf[buf.length () - 1] != '"')) + { + if (! (client_handler >> buf)) + break; + + if (buf.length () > 0) + ACE_DEBUG ((LM_DEBUG, + "%s ", + buf.c_str ())); + } + + ACE_DEBUG ((LM_DEBUG, + ")\n")); +#else + char buf[BUFSIZ]; + ACE_OS::memset (buf, 0, sizeof buf); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Received: ("))); + + while (ACE_OS::strlen (buf) == 0 + || buf[ACE_OS::strlen (buf) - 1] != '"') + { + if (! (client_handler >> buf)) + break; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s "), + ACE_TEXT_CHAR_TO_TCHAR (buf))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (")\n"))); +#endif /* ACE_HAS_STRING_CLASS */ + + // Send some non-textual data to the client. We use a single + // character to separate the fields but we could have used any valid + // whitespace. The data will be sent if the iostream's buffer gets + // filled or when we flush it with an explicit client.sync () + // command or the implicit <<endl. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server sleeping\n"))); + ACE_OS::sleep (5); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Sending: 1 .12342134 666555444 23.45 -46.5e9 \n"))); + client_handler << 1 << " "; + client_handler << .12342134 << " "; + client_handler << 666555444 << " "; + client_handler << 23.45 << " "; + client_handler << -46.5e9 << " "; + client_handler << endl; + + // The client will have changed the sign of each data field and sent + // 'em all back to us. At the same time, the client used the + // precision () function to change the significant digits for + // non-integer values. + int i; + float f1, f2; + long l; + double d; + client_handler >> i >> f1 >> l >> f2 >> d; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Received: int %d float %f long %d float %f double %f\n"), + i, + f1, + (int) l, + f2, + d)); + + // check for proper received values + ACE_ASSERT (i == -1 && (f1 >= -0.13 && f1 <= -0.11) + && l == -666555444 && (f2 >= -24.0 && f2 <= -22.0) + && (d >= 45e+9 && d <= 47e+9)); + + client_handler.close (); + + return 0; +} + +static int +spawn (void) +{ + // Acceptor; + ACE_SOCK_Acceptor acceptor; + + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P|%t) %p\n"), + ACE_TEXT ("open")), + -1); +#if defined (ACE_HAS_THREADS) + else if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (server), + &acceptor, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning server thread")), + -1); + + // Wait for the client and server thread to exit. + ACE_Thread_Manager::instance ()->wait (); + +#elif !defined (ACE_LACKS_FORK) + + switch (ACE_OS::fork ("child")) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n%a"), + ACE_TEXT ("fork failed"))); + ACE_OS::_exit (-1); + case 0: // In child + { + ACE_APPEND_LOG ("IOStream_Test-children"); + ACE_INET_Addr server_addr; + + if (acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr"))); + else + client ((void *) &server_addr); + ACE_END_LOG; + break; + } + default: // In parent + server (&acceptor); + + // Allow the client to exit, then remove the Process_Mutex. + ACE_OS::wait (); + break; + } +#else + ACE_ERROR_RETURN ((LM_INFO, + ACE_TEXT ("threads *and* processes not supported on this platform\n")), + -1); +#endif /* ACE_HAS_THREADS */ + + acceptor.close (); + + return 0; +} +#endif /* !ACE_LACKS_ACE_IOSTREAM */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("IOStream_Test")); + +#if !defined (ACE_LACKS_ACE_IOSTREAM) + ACE_INIT_LOG (ACE_TEXT ("IOStream_Test-children")); + spawn (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("ACE_IOSTREAM not supported on this platform\n"))); +#endif /* !ACE_LACKS_ACE_IOSTREAM */ + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Integer_Truncate_Test.cpp b/ACE/tests/Integer_Truncate_Test.cpp new file mode 100644 index 00000000000..97f08a10d03 --- /dev/null +++ b/ACE/tests/Integer_Truncate_Test.cpp @@ -0,0 +1,377 @@ +// ============================================================================ +/** + * @file Integer_Truncate_Test.cpp + * + * $Id$ + * + * Test @c ACE_Utils::truncate_cast<> function template. + * + * @author Ossama Othman <ossama_othman at symantec dot com> + */ +// ============================================================================ + +#include "test_config.h" + +#include <ace/Truncate.h> +#include <ace/Numeric_Limits.h> + +#include <algorithm> +#include <functional> + +using namespace ACE_Utils; + +// ---------------------------------------------------- + +bool +sizeof_from_lt_sizeof_to (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running sizeof(FROM) < sizeof(TO) test\n"))); + + bool success = true; + + // signed from_type, unsigned to_type + { + typedef signed char from_type; + typedef unsigned int to_type; + + ACE_ASSERT (sizeof (from_type) < sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (truncate_cast<to_type> (f) != static_cast<to_type> (f)) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tsigned from_type / unsigned to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // unsigned from_type, signed to_type + { + typedef unsigned char from_type; + typedef signed int to_type; + + ACE_ASSERT (sizeof (from_type) < sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (truncate_cast<to_type> (f) != static_cast<to_type> (f)) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tunsigned from_type / signed to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // signed from_type, signed to_type + { + typedef signed char from_type; + typedef signed int to_type; + + ACE_ASSERT (sizeof (from_type) < sizeof (to_type)); + + from_type f1 = -1; // Should not be truncated. + from_type f2 = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (truncate_cast<to_type> (f1) != f1 + || truncate_cast<to_type> (f2) != f2) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tsigned from_type / signed to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // unsigned from_type, unsigned to_type + { + typedef unsigned char from_type; + typedef unsigned int to_type; + + ACE_ASSERT (sizeof (from_type) < sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (truncate_cast<to_type> (f) != static_cast<to_type> (f)) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tunsigned from_type / unsigned to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\t%s\n"), + success + ? ACE_TEXT ("PASSED") + : ACE_TEXT ("FAILED"))); + + return success; +} + +bool +sizeof_from_eq_sizeof_to (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running sizeof(FROM) == sizeof(TO) test\n"))); + + bool success = true; + + // signed from_type, unsigned to_type + { + typedef signed int from_type; + typedef unsigned int to_type; + + ACE_ASSERT (sizeof (from_type) == sizeof (to_type)); + + from_type f1 = -1; // Should not be truncated. + from_type f2 = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (static_cast<from_type> (truncate_cast<to_type> (f1)) != f1 + || static_cast<from_type> (truncate_cast<to_type> (f2)) != f2) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tsigned from_type / unsigned to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // unsigned from_type, signed to_type + { + typedef unsigned int from_type; + typedef signed int to_type; + + ACE_ASSERT (sizeof (from_type) == sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should be truncated. + + if (truncate_cast<to_type> (f) != ACE_Numeric_Limits<to_type>::max ()) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tunsigned from_type / signed to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // signed from_type, signed to_type + { + typedef signed int from_type; + typedef signed int to_type; + + ACE_ASSERT (sizeof (from_type) == sizeof (to_type)); + + from_type f1 = -1; // Should not be truncated. + from_type f2 = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (truncate_cast<to_type> (f1) != f1 + || truncate_cast<to_type> (f2) != f2) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tsigned from_type / signed to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // unsigned from_type, unsigned to_type + { + typedef unsigned int from_type; + typedef unsigned int to_type; + + ACE_ASSERT (sizeof (from_type) == sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should not be truncated. + + if (truncate_cast<to_type> (f) != f) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tunsigned from_type / unsigned to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\t%s\n"), + success + ? ACE_TEXT ("PASSED") + : ACE_TEXT ("FAILED"))); + + return success; +} + +bool +sizeof_from_gt_sizeof_to (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running sizeof(FROM) > sizeof(TO) test\n"))); + + bool success = true; + + // signed from_type, unsigned to_type + { + typedef signed int from_type; + typedef unsigned char to_type; + + ACE_ASSERT (sizeof (from_type) > sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should be truncated. + + if (truncate_cast<to_type> (f) != ACE_Numeric_Limits<to_type>::max ()) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tsigned from_type / unsigned to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // unsigned from_type, signed to_type + { + typedef unsigned int from_type; + typedef signed char to_type; + + ACE_ASSERT (sizeof (from_type) > sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should be truncated. + + if (truncate_cast<to_type> (f) != ACE_Numeric_Limits<to_type>::max ()) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tunsigned from_type / signed to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // signed from_type, signed to_type + { + typedef signed int from_type; + typedef signed char to_type; + + ACE_ASSERT (sizeof (from_type) > sizeof (to_type)); + + from_type f1 = -1; // Should not be truncated. + from_type f2 = + ACE_Numeric_Limits<from_type>::max (); // Should be truncated. + + if (truncate_cast<to_type> (f1) != f1 + || truncate_cast<to_type> (f2) != ACE_Numeric_Limits<to_type>::max ()) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tsigned from_type / signed to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + // unsigned from_type, unsigned to_type + { + typedef unsigned int from_type; + typedef unsigned char to_type; + + ACE_ASSERT (sizeof (from_type) > sizeof (to_type)); + + from_type f = + ACE_Numeric_Limits<from_type>::max (); // Should be truncated. + + if (truncate_cast<to_type> (f) != ACE_Numeric_Limits<to_type>::max ()) + { + success = false; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tunsigned from_type / unsigned to_type ") + ACE_TEXT ("truncation test failed"))); + } + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("\t%s\n"), + success + ? ACE_TEXT ("PASSED") + : ACE_TEXT ("FAILED"))); + + return success; +} + +// ---------------------------------------------------- + +/** + * @struct Caller + * + * @brief Test method invocation functor. + * + * Test method invocation functor. + */ +template <typename T> +struct Caller : public std::unary_function<T, void> +{ + /// Constructor + Caller (void) : success (true) {} + + /// Function call operator overload. + void operator() (T f) + { + if (!f ()) + success = false; + } + + /// Flag that indicates success of all tests. + bool success; +}; + +// ---------------------------------------------------- + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Integer_Truncate_Test")); + + typedef bool (*test_func) (); + + static test_func const tests[] = + { + sizeof_from_lt_sizeof_to + , sizeof_from_eq_sizeof_to + , sizeof_from_gt_sizeof_to + }; + + static size_t const test_count = sizeof (tests) / sizeof (tests[0]); + + // Have some fun with the STL. :-) + Caller<test_func> c = + std::for_each (tests, + tests + test_count, + Caller<test_func> ()); + + ACE_END_TEST; + + return c.success ? 0 : -1; +} diff --git a/ACE/tests/Lazy_Map_Manager_Test.cpp b/ACE/tests/Lazy_Map_Manager_Test.cpp new file mode 100644 index 00000000000..d7434bbada0 --- /dev/null +++ b/ACE/tests/Lazy_Map_Manager_Test.cpp @@ -0,0 +1,335 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Lazy_Map_Manager_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the <ACE_Map_Manager> and +// <ACE_Active_Map_Manager> that illustrates how lazy map managers +// allow the deletion of entries while iterating over the map. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu>, +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Map_Manager.h" +#include "ace/Active_Map_Manager.h" + +ACE_RCSID(tests, Map_Manager_Test, "$Id$") + +// Simple map manager. +typedef ACE_Map_Manager<int, int, ACE_Null_Mutex> LAZY_MAP; + +// Displaying the contents of a map manager. + +void +display_map (LAZY_MAP &map) +{ + { + // Simple iteration printing the entries. + for (LAZY_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + LAZY_MAP::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%d "), + entry.int_id_)); + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + } + + { + // Simple reverse iteration printing the entries. + for (LAZY_MAP::reverse_iterator iter = map.rbegin (); + iter != map.rend (); + ++iter) + { + LAZY_MAP::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%d "), + entry.int_id_)); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); +} + +// Test for map manager. + +void +map_test (void) +{ + // Map of size 3. + LAZY_MAP map (3); + int i = 0; + + // Insert a few entries. + for (i = 0; i < 3; ++i) + map.bind (i, i); + + display_map (map); + + // Remove middle one. + map.unbind (1); + + display_map (map); + + // Remove the entry on one end. + map.unbind (0); + + display_map (map); + + // Remove the entry on the other end. + map.unbind (2); + + display_map (map); + + // If we have lazy map managers, we can delete entries while + // iterating over the map. + +#if defined (ACE_HAS_LAZY_MAP_MANAGER) + + // Insert a few entries. + for (i = 0; i < 3; ++i) + map.bind (i, i); + + display_map (map); + + // Remove middle one. + { + // Deletion while iterating. + for (LAZY_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + LAZY_MAP::ENTRY &entry = *iter; + if (entry.int_id_ == 1) + map.unbind (1); + } + + display_map (map); + } + + // Remove the entry on one end. + { + // Deletion while iterating. + for (LAZY_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + LAZY_MAP::ENTRY &entry = *iter; + if (entry.int_id_ == 0) + map.unbind (0); + } + + display_map (map); + } + + // Remove the entry on the other end. + { + // Deletion while iterating. + for (LAZY_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + LAZY_MAP::ENTRY &entry = *iter; + if (entry.int_id_ == 2) + map.unbind (2); + } + + display_map (map); + } + +#endif /* ACE_HAS_LAZY_MAP_MANAGER */ + + // Insert a few entries. This will force an increase in map size. + for (i = 0; i < 4; ++i) + map.bind (i, i); + + display_map (map); + + // Remove a few entries (in reverse order). + for (i = 3; i >= 0; --i) + map.unbind (i); + + display_map (map); +} + +// Simple active map manager. +typedef ACE_Active_Map_Manager<int> ACTIVE_MAP; + +// Displaying the contents of an active map manager. + +void +display_map (ACTIVE_MAP &map) +{ + { + // Simple iteration printing the entries. + for (ACTIVE_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + ACTIVE_MAP::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%d "), + entry.int_id_)); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + } + + { + // Simple reverse iteration printing the entries. + for (ACTIVE_MAP::reverse_iterator iter = map.rbegin (); + iter != map.rend (); + ++iter) + { + ACTIVE_MAP::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%d "), + entry.int_id_)); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); +} + +// Test for active map manager. + +void +active_map_test (void) +{ + // Map of size 3. + ACTIVE_MAP map (3); + ACE_Active_Map_Manager_Key keys[4]; + int i = 0; + + // Insert a few entries. + for (i = 0; i < 3; ++i) + map.bind (i, keys[i]); + + display_map (map); + + // Remove middle one. + map.unbind (keys[1]); + + display_map (map); + + // Remove the entry on one end. + map.unbind (keys[0]); + + display_map (map); + + // Remove the entry on the other end. + map.unbind (keys[2]); + + display_map (map); + + // If we have lazy map managers, we can delete entries while + // iterating over the map. + +#if defined (ACE_HAS_LAZY_MAP_MANAGER) + + // Insert a few entries. + for (i = 0; i < 3; ++i) + map.bind (i, keys[i]); + + display_map (map); + + // Remove middle one. + { + // Deletion while iterating. + for (ACTIVE_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + ACTIVE_MAP::ENTRY &entry = *iter; + if (entry.int_id_ == 1) + map.unbind (keys[1]); + } + + display_map (map); + } + + // Remove the entry on one end. + { + // Deletion while iterating. + for (ACTIVE_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + ACTIVE_MAP::ENTRY &entry = *iter; + if (entry.int_id_ == 0) + map.unbind (keys[0]); + } + + display_map (map); + } + + // Remove the entry on the other end. + { + // Deletion while iterating. + for (ACTIVE_MAP::iterator iter = map.begin (); + iter != map.end (); + ++iter) + { + ACTIVE_MAP::ENTRY &entry = *iter; + if (entry.int_id_ == 2) + map.unbind (keys[2]); + } + + display_map (map); + } + +#endif /* ACE_HAS_LAZY_MAP_MANAGER */ + + // Insert a few entries. This will force an increase in map size. + for (i = 0; i < 4; ++i) + map.bind (i, keys[i]); + + display_map (map); + + // Remove a few entries (in reverse order). + for (i = 3; i >= 0; --i) + map.unbind (keys[i]); + + display_map (map); +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Lazy_Map_Manager_Test")); + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nMap Manager...\n\n"))); + map_test (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nActive Map Manager...\n\n"))); + active_map_test (); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Log_Msg_Backend_Test.cpp b/ACE/tests/Log_Msg_Backend_Test.cpp new file mode 100644 index 00000000000..709978e8ef9 --- /dev/null +++ b/ACE/tests/Log_Msg_Backend_Test.cpp @@ -0,0 +1,160 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Log_Msg_Backend_Test.cpp +// +// = DESCRIPTION +// This program tests the ACE_Log_Msg class's use of a custom backend, +// including its initialization and reset as well as logging information +// through the backend. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" + +#include "ace/Log_Msg.h" +#include "ace/Log_Msg_Backend.h" +#include "ace/Log_Record.h" + +ACE_RCSID(tests, Log_Msg_Backend_Test, "$Id$") + +class Backend : public ACE_Log_Msg_Backend +{ +public: + Backend () + : reset_ (false), open_ (false), close_ (false), log_count_ (0) {} + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (const ACE_TCHAR *logger_key); + //FUZZ: enable check_for_lack_ACE_OS + + virtual int reset (void); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int close (void); + //FUZZ: enable check_for_lack_ACE_OS + + virtual ssize_t log (ACE_Log_Record &log_record); + + // Test probes to see if things worked as specified. + bool hooks_ok (void) const; + unsigned int log_count (void) const { return this->log_count_; } + +private: + bool reset_; + bool open_; + bool close_; + unsigned int log_count_; +}; + +int +Backend::open (const ACE_TCHAR *) +{ + this->open_ = true; + return 0; +} + +int +Backend::reset (void) +{ + this->reset_ = true; + return 0; +} + +int +Backend::close (void) +{ + return 0; +} + +ssize_t +Backend::log (ACE_Log_Record &) +{ + ++this->log_count_; + return 1; +} + +// Test probes to see if things worked as specified. +bool +Backend::hooks_ok (void) const +{ + // Close should not have been called yet. + const ACE_TCHAR *yes = ACE_TEXT ("yes"); + const ACE_TCHAR *no = ACE_TEXT ("no"); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Open: %s, Reset: %s, Close: %s\n"), + this->open_ ? yes : no, + this->reset_ ? yes : no, + this->close_ ? yes : no)); + return this->open_ && this->reset_ && !this->close_; +} + +// Main function. + +int +run_main (int, ACE_TCHAR *[]) +{ + // Set up the backend prior to ACE_START_TEST so the initialization can + // call the back end's reset(). + Backend b; + ACE_Log_Msg_Backend *old_b; + old_b = ACE_Log_Msg::msg_backend (&b); + + ACE_START_TEST (ACE_TEXT ("Log_Msg_Backend_Test")); + + int status = 0; + + if (old_b != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Old backend (%@) not 0 at start\n"), + old_b)); + ++status; + } + + // Reopen to get the backend established. + u_long flags = ACE_LOG_MSG->flags (); + flags |= ACE_Log_Msg::CUSTOM; + if (-1 == ACE_LOG_MSG->open (ACE_TEXT ("Log_Msg_Backend_Test"), flags)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("Reopening log"))); + ++status; + } + + // Make sure messages are getting routed correctly. + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Message 1\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Message 2\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Message 3\n"))); + unsigned int count = b.log_count (); + if (count != 3) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Backend counted %u; expected 3\n"), + count)); + ++status; + } + + // Should have seen the hooks other than close() so far. Note that + // hooks_ok() logs a message so check counts before checking hooks. + if (!b.hooks_ok ()) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Hooks not called correctly\n"))); + ++status; + } + + ACE_END_TEST; + + // Reset the backend to avoid references to our soon-to-be-destroyed one. + ACE_Log_Msg::msg_backend (old_b); + + return status; +} + diff --git a/ACE/tests/Log_Msg_Test.cpp b/ACE/tests/Log_Msg_Test.cpp new file mode 100644 index 00000000000..83210c6a59f --- /dev/null +++ b/ACE/tests/Log_Msg_Test.cpp @@ -0,0 +1,625 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Log_Msg_Test.cpp +// +// = DESCRIPTION +// This program tests the <ACE_Log_Msg> class in various ways and +// also illustrates many of the features of the <ACE_Log_Msg> For +// instance, this program tests the <ACE_Log_Msg> abstraction wrt +// writing to stderr and to a file. It also tests writing to user +// defined callback objects. +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/FILE_Connector.h" +#include "ace/Auto_Ptr.h" +#include "ace/Log_Msg_Callback.h" +#include "ace/Log_Record.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_Memory.h" + +ACE_RCSID(tests, Log_Msg_Test, "$Id$") + +static void +cleanup (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("cleanup hook (%P)!\n"))); +} + +static void +cause_error (void) +{ + errno = EWOULDBLOCK; + ACE_ERROR ((LM_DEBUG, + ACE_TEXT ("would block\n"))); +} + +class Logger : public ACE_Log_Msg_Callback +{ +public: + Logger (int be_recursive = 1); + // Constructor sets whether we're testing "recursive" callback + // logging! + + void log (ACE_Log_Record &log_record); + // Logging callback + + void verbose (int be_verbose); + +private: + int verbose_logging_; + // Flag for testing verbose logging. + + int recursive_; + // Flag for testing recursive callback logging. +}; + +void +Logger::verbose (int be_verbose) +{ + this->verbose_logging_ = be_verbose; +} + +Logger::Logger (int be_recursive) + : recursive_ (be_recursive) +{ +} + +void +Logger::log (ACE_Log_Record &log_record) +{ + int use_log_msg = 0; + if (this->recursive_) + { + this->recursive_ = 0; + use_log_msg = 1; + } + + if (!this->verbose_logging_) + { + if (use_log_msg) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Logger::log->%s\n"), + log_record.msg_data ())); +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + else + *ace_file_stream::instance ()->output_file () + << "Recursive Logger callback = " + << log_record.msg_data () + << endl; +#endif /* ACE_LACKS_IOSTREAM_TOTALLY */ + } + else + { + ACE_TCHAR verbose_msg[ACE_Log_Record::MAXVERBOSELOGMSGLEN]; + int result = log_record.format_msg (ACE_LOG_MSG->local_host (), + ACE_LOG_MSG->flags (), + verbose_msg); + if (result == 0) + { + if (use_log_msg) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Logger::log->%s\n"), + verbose_msg)); +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + else + *ace_file_stream::instance ()->output_file () + << "Recursive Logger callback = " + << log_record.msg_data () + << endl; +#endif /* ACE_LACKS_IOSTREAM_TOTALLY */ + } + } + + // Cleanup on the way out. + if (use_log_msg) + this->recursive_ = 1; +} + +static void +test_callbacks (void) +{ + // This message should show up in stderr. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) first message\n"))); + + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::OSTREAM); + + // This message should not show up anywhere. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) second message\n"))); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::MSG_CALLBACK); + + // This message should not show up anywhere since no callback object + // has been specified. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) third message\n"))); + + // Create a callback object and make it "verbose". + Logger logger; + logger.verbose (1); + + // Set the callback object. + ACE_LOG_MSG->msg_callback (&logger); + + // This message should show up via the Logger callback. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) forth message\n"))); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + + // This message should show up via the Logger callback (somewhat + // verbosely). + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) fifth message\n"))); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE); + + // This message should show up via the Logger callback (really + // verbosely). + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) sixth message\n"))); + + logger.verbose (0); + + // This message should show up via the Logger callback (not + // verbosely). + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) seventh message\n"))); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM); + + // This message should show up in stderr and the Logger callback. + // The one from the Logger callback will not be verbose, but the one + // from stderr should be verbose. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) eighth message\n"))); + ACE_LOG_MSG->msg_callback (0); +} + +static void +test_log_msg_features (const ACE_TCHAR *program) +{ + // Note that the default behavior is to log to STDERR... + + int counter = 1 ; + + if (ACE_LOG_MSG->open (program) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("cannot open logger!!!\n"))); + + cause_error (); + + // Check to see what happened. + if (ACE_LOG_MSG->op_status () == -1 + && ACE_LOG_MSG->errnum () == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("op_status and errnum work!\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("op_status and errnum failed!\n"))); + + const char *badname = "badname"; + + // We use the DEBUG messages instead of error messages. This is to + // help the scripts. If we print out error messages the scripts + // start catching them as errors. + if (ACE_OS::open (badname, + O_RDONLY) == ACE_INVALID_HANDLE) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%n: (%x), can't open %C%r\n"), + 10000, + badname, + cleanup)); + +// Don't try this on VxWorks, it will result in an overflow and end the test. +// Platforms that define ACE_LACKS_VSNPRINTF are candidates to fail here. +// This then proves that logging to big messages is problematic but on VxWorks +// we know this and we want to rest of the test to continue +#if !defined (VXWORKS) && !defined (__Lynx__) + // Try a log operation that would overflow the logging buffer if not + // properly guarded. + ACE_TCHAR big[ACE_Log_Record::MAXLOGMSGLEN + 1]; + size_t i = 0; + static const ACE_TCHAR alphabet[] = ACE_TEXT ("abcdefghijklmnopqrstuvwxyz"); + size_t j = ACE_OS::strlen (alphabet); + while (i < ACE_Log_Record::MAXLOGMSGLEN) + { + size_t const index = i++; + big[index] = alphabet[i % j]; + } + ACE_DEBUG ((LM_INFO, ACE_TEXT ("This is too big: %s\n"), big)); +#endif /* !VXWORKS */ + + // Exercise many different combinations of OSTREAM. + + double f = 3.1416 * counter++; + int n = 10000; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%10f, %*s%s = %d\n"), + f, + 8, + ACE_TEXT (""), + ACE_TEXT ("hello"), + n)); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM); + ACE_LOG_MSG->msg_ostream (ace_file_stream::instance ()->output_file ()); + + f = 3.1416 * counter; + n = 10000 * counter++; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%10f, %*s%s = %d\n"), + f, + 8, + ACE_TEXT (""), + ACE_TEXT ("world"), + n)); + + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::OSTREAM); + + // The next two messages shouldn't print. + + f = 3.1416 * counter; + n = 10000 * counter++; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%10f, %*s%s = %d\n"), + f, + 8, + ACE_TEXT (""), + ACE_TEXT ("world"), + n)); + + f = 3.1416 * counter; + n = 10000 * counter++; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%10f, %*s%s = %d\n"), + f, + 8, + ACE_TEXT (""), + ACE_TEXT ("world"), + n)); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM); + + f = 3.1416 * counter; + n = 10000 * counter++; + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%10f, %*s%s = %d\n"), + f, + 8, + ACE_TEXT (""), + ACE_TEXT ("world"), + n)); + + static int array[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}; + + // Print out the binary bytes of the array in hex form. + ACE_LOG_MSG->log_hexdump (LM_DEBUG, + (char *) array, + sizeof array); + + // Disable the LM_DEBUG and LM_INFO messages. + u_long priority_mask = + ACE_LOG_MSG->priority_mask (ACE_Log_Msg::PROCESS); + ACE_CLR_BITS (priority_mask, + LM_DEBUG | LM_INFO); + ACE_LOG_MSG->priority_mask (priority_mask, + ACE_Log_Msg::PROCESS); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("This LM_INFO message should not print!\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This LM_DEBUG message should not print!\n"))); + + ACE_SET_BITS (priority_mask, + LM_INFO); + ACE_LOG_MSG->priority_mask (priority_mask, + ACE_Log_Msg::PROCESS); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("This LM_INFO message should print!\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This LM_DEBUG message should not print!\n"))); + + ACE_CLR_BITS (priority_mask, LM_INFO); + ACE_LOG_MSG->priority_mask (priority_mask, + ACE_Log_Msg::PROCESS); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("This LM_INFO message should not print!\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This LM_DEBUG message should not print!\n"))); +} + +static int +test_ostream (void) +{ + // This message should show up in the log file. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("first message\n"))); + + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::OSTREAM); + + // This message should not show up anywhere. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("second message\n"))); + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM); + +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + // Create a persistent store. + const ACE_TCHAR *filename = ACE_TEXT ("output"); + ofstream myostream (ACE_TEXT_ALWAYS_CHAR (filename), ios::out | ios::trunc); + + // Check for errors. + if (myostream.bad ()) + return -1; + + OFSTREAM *old_stream = ace_file_stream::instance ()->output_file (); + // Set the ostream. + ACE_LOG_MSG->msg_ostream (&myostream); + + // This message should show up in the ostream. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("fourth message\n"))); + // Set the ostream back to the test's log file. + ACE_LOG_MSG->msg_ostream (old_stream); + // Now close the ostream file and check its contents. + myostream.close (); + + ACE_FILE_Connector connector; + ACE_FILE_IO file; + ACE_FILE_Addr file_addr (filename); + + // Open up the file. + if (connector.connect (file, file_addr) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("connect failed for %p\n"), + filename), + 1); + } + +#if !defined (ACE_VXWORKS) && !defined (ACE_HAS_PHARLAP) || (defined(ACE_VXWORKS) && (ACE_VXWORKS >= 0x650)) +# define TEST_CAN_UNLINK_IN_ADVANCE +#endif + +#if defined (TEST_CAN_UNLINK_IN_ADVANCE) + // Unlink this file right away so that it is automatically removed + // when the process exits.Ignore error returns in case this operation + // is not supported. + ACE_OS::unlink(filename); +#endif + + ACE_FILE_Info info; + if (file.get_info (info) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("get_info failed on %p\n"), + filename), + -1); + } + + // Allocate the input buffer + char *buffer = 0; + ACE_NEW_RETURN (buffer, + char[info.size_ + 1], + -1); + // Make sure <buffer> is released automagically. + ACE_Auto_Basic_Array_Ptr<char> b (buffer); + + // Read the file into the buffer. + ssize_t size = file.recv (buffer, + info.size_); + if (size != info.size_) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Read %d bytes, rather than expected %d bytes\n"), + size, + info.size_), + -1); + } + // Make sure to NUL-terminate this turkey! + buffer[size] = '\0'; + + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%C"), + buffer)); + +#if !defined (TEST_CAN_UNLINK_IN_ADVANCE) + file.close (); + if (file.unlink () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unlink failed for %p\n"), + file_addr.get_path_name ()), + 1); +#endif + +#endif /* ACE_LACKS_IOSTREAM_TOTALLY */ + + // This message should show up in stderr and the ostream (without + // ACE_LACKS_IOSTREAM_TOTALLY). + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("fifth message\n"))); + + return 0; +} + + +// For testing the format specifiers, a class is defined as a callback +// mechanism. It will get the formatted messages and check them for +// correctness. The test_format_specs() function will set the first +// few characters to say which test is being run, and the Log_Spec_Verify +// class will use that to decide how to verify the results. + +class Log_Spec_Verify : public ACE_Log_Msg_Callback +{ +public: + Log_Spec_Verify () : fail_ (0) {}; + + void log (ACE_Log_Record &log_record); + // Logging callback + + int result (); + +private: + int fail_; + // Count how many tests failed. +}; + +void +Log_Spec_Verify::log (ACE_Log_Record &log_record) +{ + const ACE_TCHAR *b = log_record.msg_data (); + const ACE_TCHAR *expect = 0; + + if (ACE_OS::strncmp (b, ACE_TEXT ("l1:"), 3) == 0) + { + expect = ACE_TEXT ("42"); + b += 4; //3 + } + else if (ACE_OS::strncmp (b, ACE_TEXT ("l2:"), 3) == 0) + { + expect = ACE_TEXT (" 42"); + b += 3; + } + else if (ACE_OS::strncmp (b, ACE_TEXT ("l3N1:"), 4) == 0) + { + expect = ACE_TEXT ("0042,Log_Msg"); + b += 4; + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Log_Spec_Verify, unrecognized test: %s\n"), + b)); + ++this->fail_; + } + + if (b != log_record.msg_data () && ACE_OS::strcmp (b, expect) != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Test %s failed; expected %s\n"), + log_record.msg_data (), expect)); + ++this->fail_; + } + + return; +} + +int +Log_Spec_Verify::result (void) +{ + if (this->fail_ == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("All logging specifier tests passed.\n"))); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%d logging specifier tests failed!\n"), + this->fail_)); + return this->fail_; +} + +static int +test_format_specs (void) +{ +#if 0 + Log_Spec_Verify verifier; + ACE_Log_Msg logger; + + if (logger.open (ACE_TEXT ("Log_Msg_Test"), ACE_Log_Msg::MSG_CALLBACK) != 0) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("%T: test_format_specs open")), + 1); + logger.msg_callback (&verifier); + + logger.linenum (42); + logger.file (ACE_TEXT ("Log_Msg_Test.cpp")); + logger.log (LM_DEBUG, ACE_TEXT ("l1:%l")); + logger.log (LM_DEBUG, ACE_TEXT ("l2:%5l")); + logger.log (LM_DEBUG, ACE_TEXT ("l3N1:%0*l,%.7N"), 4); + return verifier.result (); +#else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("l1:%l\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("l2:%5l\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("l3N1:%0*l,%.7N\n"), 4)); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%*ISTART INDENTING %{\n"), 4)); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%IONE%{\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%ITWO%{\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%ITHREE\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%}%ITWO\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%}%IONE\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%}%IENDINDENTING\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%W\n"), ACE_TEXT_WIDE ("My string test\n"))); + ACE_TCHAR* nill_string = 0; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%W\n"), nill_string)); + errno = ENOENT; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%m %p\n"), ACE_TEXT("perror"))); + return 0; +#endif +} + +// Main function. + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Log_Msg_Test")); + + int status = 0; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** running ostream test\n"))); + + // Test the <ACE_Log_Msg> abstraction wrt writing to stderr and to a + // file. + test_ostream (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%M **** running callback test\n"))); + + // Test the <ACE_Log_Msg> callback mechanism. + test_callbacks (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** running features test\n"))); + + // Test various features of the <ACE_Log_Msg>. + test_log_msg_features ((argc > 0 ? argv[0] : ACE_TEXT ("program"))); + + // Test the format specifiers + + // Restore this mask so diags and the shutdown message will print correctly! + ACE_LOG_MSG->priority_mask (ACE_LOG_MSG->priority_mask () | LM_DEBUG, + ACE_Log_Msg::PROCESS); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** running format specifiers test\n"))); + if (test_format_specs () != 0) + status = 1; + + ACE_END_TEST; + return status; +} + diff --git a/ACE/tests/Logging_Strategy_Test.cpp b/ACE/tests/Logging_Strategy_Test.cpp new file mode 100644 index 00000000000..6e3739020dd --- /dev/null +++ b/ACE/tests/Logging_Strategy_Test.cpp @@ -0,0 +1,517 @@ +// $Id$ + +//========================================================================== +// +// = LIBRARY +// tests +// +// = FILENAME +// Logging_Strategy_Test.cpp +// +// = DESCRIPTION +// This program tests the <ACE_Logging_Strategy> class in various +// ways and also illustrates many of the features of the +// <ACE_Log_Msg>. The test works as follows: +// -Load the inserted arguments; +// -Remove existent log_files with the file_name specified by the +// user; +// -Generate 1000 messages to create the DEBUG statements to be +// stored in the files; +// -Counts the created log_files and if it was specified a maximum +// number of log_files, compare and verify if they are the same. +// -Verify the order of the files with the order argument. +// +// When Dlls are used, we utilize the dynamic service +// configuration mechanism to activate the logging strategy. This +// is not a must though, and you may activate the logging strategy +// as described in the non-DLL section below under DLL +// environments as well. +// +// = AUTHOR +// Orlando Ribeiro <oribeiro@inescporto.pt> +// +//========================================================================== + +#include "test_config.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/Auto_Ptr.h" +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Thread_Manager.h" + +#if defined (ACE_AS_STATIC_LIBS) || \ + (!defined (ACE_WIN32) && !defined (ACE_HAS_SVR4_DYNAMIC_LINKING) && \ + !defined (__hpux)) +#include "ace/Logging_Strategy.h" +#endif + +#include "ace/Auto_Ptr.cpp" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_time.h" + +ACE_RCSID(tests, Logging_Strategy_Test, "$Id$") + +// Considering UNIX OS to be default. On Win32 platforms, the symbols +// are got form the .exe as one cant have .exe and .dll for the same +// .cpp. Also, on Win32 platforms one cant use the .obj to obtain +// symbols dynamically at runtime. + +#if defined (ACE_WIN32) +# define OBJ_SUFFIX ACE_TEXT (".exe") +# define OBJ_PREFIX ACE_TEXT ("") +#else +# define OBJ_SUFFIX ACE_DLL_SUFFIX +# define OBJ_PREFIX "./" ACE_DLL_PREFIX +#endif /*ACE_WIN32*/ + +ACE_TCHAR const * +cdecl_decoration (ACE_TCHAR const *func_name) +{ +#if defined (__BORLANDC__) + static ACE_TCHAR decorated_func_name[10*1024]; + ACE_OS::sprintf (decorated_func_name, + ACE_TEXT ("_%s"), + func_name); + return decorated_func_name; +#else + return func_name; +#endif /* __BORLANDC__ */ +} + +// Global variables. +static ACE_TCHAR *file_name = 0; +static int max_size_files = 0; +static int max_num_files = 0; +static int interval_time = 0; +static bool order_state = false; +static int num_files = 0; +static bool wipeout_logfile = false; + +// This adapter function runs the Reactor's event loop in a separate +// thread of control. + +static void * +run_reactor (void *) +{ + ACE_Reactor::instance ()->owner + (ACE_Thread_Manager::instance ()->thr_self ()); + ACE_Reactor::instance ()->run_reactor_event_loop (); + return 0; +} + +// Initiate the cycle of messages. + +static +void print_till_death (void) +{ + ACE_DEBUG ((LM_DEBUG, + "\n-> start generating messages... \n")); + + for (int i = 0; i < 1000; i++) + { + if (i % 50 == 0) + ACE_OS::sleep (1); + + if (i == 0) + ACE_DEBUG ((LM_DEBUG, + " (%t) (%D) message\n")); + else + ACE_DEBUG ((LM_DEBUG, + " (%t) %d message\n", + i)); + } + + if (ACE_Reactor::instance ()->end_reactor_event_loop () == -1) + ACE_ERROR ((LM_ERROR, + "Error ending reactor.\n")); + + ACE_DEBUG ((LM_DEBUG, + "-< generating messages finished \n\n")); +} + +// Count the generated files. + +static void +count_files (void) +{ + int i = 0; + int error = 0; + FILE *stream; + ACE_TCHAR backup_ct[MAXPATHLEN+1]; + ACE_DEBUG ((LM_DEBUG, + "-> start counting...\n")); + + do + { + if (i == 0) + ACE_OS::sprintf (backup_ct, + ACE_TEXT ("%s"), + file_name); + else + ACE_OS::sprintf (backup_ct, + ACE_TEXT ("%s.%d"), + file_name, + i); + + stream = ACE_OS::fopen (backup_ct, ACE_TEXT ("r")); + if (stream == 0) + error = 1; + else + { + i++; + ACE_OS::fclose (stream); + } + + } + while (error != 1); + + num_files = i; + + if (max_num_files !=0) + { + if (max_num_files != num_files) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Creating files...Failed!") + ACE_TEXT (" Input value=%d, Checked value=%d"), + max_num_files, + num_files)); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Creating files...OK!") + ACE_TEXT (" Input value=%d, Checked value=%d"), + max_num_files, + num_files)); + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" The number of files generated is: %d"), + num_files)); + + ACE_DEBUG ((LM_DEBUG, + "\n-< counting finished... \n")); +} + +// get the file statistics + +static time_t +get_statistics (ACE_TCHAR *f_name) +{ + ACE_stat buf; + int result; + + // Get data associated with "file_name": + result = ACE_OS::stat (f_name, &buf); + + // Check if statistics are valid: + if (result != 0) + ACE_OS::perror (ACE_TEXT ("\nProblem getting information")); + else + { + // Output some of the statistics: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" File name : %s\n"), + f_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" File size (B): %d\n"), + buf.st_size)); + +#if defined (ACE_HAS_WINCE) + time_t tm = buf.st_mtime.sec(); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Time modified : %s\n"), + ACE_OS::ctime (&tm))); +#else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Time modified : %s\n"), + ACE_OS::ctime (&buf.st_mtime))); +#endif // ACE_HAS_WINCE + } + +#if defined (ACE_HAS_WINCE) + return buf.st_mtime.sec(); +#else + return buf.st_mtime; +#endif // ACE_HAS_WINCE +} + +// analyse the file order +static void +order (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n-> start testing order... \n"))); + + if (num_files <= 2) + { + if (num_files == 1) + get_statistics (file_name); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Ordering...OK! - ") + ACE_TEXT (" Only %d file (s) was (were) generated"), + num_files)); + } + else + { + time_t tm_bk_1, tm_bk_2; + ACE_TCHAR backup_1[MAXPATHLEN+1]; + ACE_TCHAR backup_2[MAXPATHLEN+1]; + ACE_OS::sprintf (backup_1, + ACE_TEXT ("%s.%d"), + file_name, + 1); + ACE_OS::sprintf (backup_2, + ACE_TEXT ("%s.%d"), + file_name, + num_files - 1); + + tm_bk_1 = get_statistics (backup_1); + tm_bk_2 = get_statistics (backup_2); + + if (tm_bk_1 > tm_bk_2 && !order_state) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" %s (newest) ; %s (oldest)\n"), + backup_1, + backup_2)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Ordering...OK!"))); + } + else + { + if (tm_bk_1 < tm_bk_2 && order_state) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" %s (newest);") + ACE_TEXT ("%s (oldest)\n"), + backup_2, + backup_1)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Ordering...OK!"))); + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Ordering...FAILED!") + ACE_TEXT ("- The files are disorderly"))); + } + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n-< testing order finished...\n\n"))); +} + +// remove log_files + +static void +remove_files (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("-> removing existent files...\n"))); + + int error = 0; + int test; + int i = 0; + ACE_TCHAR backup[MAXPATHLEN+1]; + + do + { + i++; + ACE_OS::sprintf (backup, + ACE_TEXT ("%s.%d"), + file_name, + i); + test = ACE_OS::unlink (backup); + if (test != 0) + error = 1; + } + while (error != 1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("-< removing existing files...\n\n"))); +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Specifications:\n"))); + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("s:i:m:f:N:ow")); + int c; + + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 's': + file_name = get_opt.opt_arg (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("File name: %s\n"), + file_name)); + break; + case 'i': + interval_time = ACE_OS::atoi (get_opt.opt_arg ()); + + //FUZZ: disable check_for_lack_ACE_OS + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Interval time (s): %d\n"), + interval_time)); + //FUZZ: enable check_for_lack_ACE_OS + break; + case 'm': + max_size_files = ACE_OS::atoi (get_opt.opt_arg ()); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Maximum size (KB): %d\n"), + max_size_files)); + break; + case 'f': + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Modes: %s\n"), + get_opt.opt_arg ())); + break; + case 'N': + max_num_files = ACE_OS::atoi (get_opt.opt_arg ()); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Maximum files number: %d\n"), + max_num_files)); + break; + case 'o': + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Ordering files activated\n"))); + order_state = true; + break; + case 'w': + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Wipeout logfile activated\n"))); + wipeout_logfile = true; + break; + default: + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("usage: [-s]<file_name>") + ACE_TEXT ("[-i]<sample_interval> ") + ACE_TEXT ("[-m]<max_size> [-f]<msg_flags> ") + ACE_TEXT ("[-n]<num_files> [-o]\n") + ACE_TEXT ("\t-s: Specify the name of the log files.\n") + ACE_TEXT ("\t-i: Define the sample interval in secs.\n") + ACE_TEXT ("\t-m: Define the max size for the log_files in KB.\n") + ACE_TEXT ("\t-f: Indicates the Log_Msg flags.\n") + ACE_TEXT ("\t-N: Define the maximum number of log_files.\n") + ACE_TEXT ("\t-o: If activated puts the log_files ordered.\n"), + ACE_TEXT ("\t-w: If activated cause the logfile to be wiped out,") + ACE_TEXT (" both on startup and on reconfigure.\n")), + -1); + /* NOTREACHED */ + break; + } + } + + ACE_UNUSED_ARG (wipeout_logfile); + return 0; +} + +int run_main (int argc, ACE_TCHAR *argv []) +{ + ACE_START_TEST (ACE_TEXT ("Logging_Strategy_Test")); + + ACE_TCHAR *l_argv[4]; + + if (argc > 1) + { + if (parse_args (argc, argv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Invalid command-line parameters.\n"), + 1); + } + else + { + l_argv[0] = (ACE_TCHAR *)ACE_TEXT ("Logging_Strategy_Test"); + l_argv[1] = + (ACE_TCHAR *) ACE_TEXT ("-slog/Logging_Strategy_Test") + ACE_LOG_FILE_EXT_NAME; + l_argv[2] = (ACE_TCHAR *) ACE_TEXT ("-o"); + l_argv[3] = 0; + + if (parse_args (3, l_argv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Invalid command-line parameters.\n"), + 1); + argv = l_argv; + argc = 3; + } + + // Remove existing files. + remove_files (); + + // This is necessary only if the provided logfile name is the same + // as the default name. If so, nothing will be written as the + // previous ofstream is closed only at the end (ACE_END_TEST) + ACE_CLOSE_TEST_LOG; + + // When Dlls are used, we utilize the dynamic service configuration + // mechanism to activate the logging strategy. This is not a must + // though, and you may activate the logging strategy as described in + // the non-DLL section below under DLL environments as well. + +#if !defined (ACE_AS_STATIC_LIBS) && \ + (defined (ACE_WIN32) || defined (ACE_HAS_SVR4_DYNAMIC_LINKING) || \ + defined (__hpux)) + + // Platform support DLLs, and not configured to link statically + ACE_TCHAR arg_str[250]; + ACE_OS::sprintf (arg_str, + ACE_TEXT ("dynamic Logger Service_Object ") + ACE_TEXT ("*ACE:_make_ACE_Logging_Strategy()") + ACE_TEXT ("\"")); + + for (int i = 1; i < argc; i++) + { + ACE_OS::strcat (arg_str, argv[i]); + ACE_OS::strcat (arg_str, ACE_TEXT (" ")); + } + + ACE_OS::strcat (arg_str, ACE_TEXT ("\"")); + + if (ACE_Service_Config::process_directive (arg_str) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Error opening _make_ACE_Log_Strategy.\n"), + 1); +#else // Platform doesn't support DLLs, or configured to link + // statically + ACE_Logging_Strategy logging_strategy; + char ls_argc = argc - 1; + ACE_Auto_Basic_Ptr<ACE_TCHAR *> ls_argv (new ACE_TCHAR *[ls_argc]); + + for (char c = 0; c < ls_argc; c++) + (ls_argv.get ())[c] = argv[c+1]; + + if (logging_strategy.init (ls_argc, ls_argv.get ()) == -1) + ACE_ERROR_RETURN + ((LM_ERROR, + "Error initializing the ACE_Logging_Strategy.\n"), + 1); +#endif /* !ACE_AS_STATIC_LIBS && (ACE_WIN32 || + ACE_HAS_SVR4_DYNAMIC_LINKING || __hpux) */ + + // launch a new Thread + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (run_reactor)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Spawning Reactor.\n"), + 1); + + // Function to print the message + print_till_death (); + + // Counts the generated files + count_files (); + + // Get the file order + order (); + + // Wait for the thread to exit before we exit. + ACE_Thread_Manager::instance ()->wait (); + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/MEM_Stream_Test.cpp b/ACE/tests/MEM_Stream_Test.cpp new file mode 100644 index 00000000000..d581fd46180 --- /dev/null +++ b/ACE/tests/MEM_Stream_Test.cpp @@ -0,0 +1,527 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MEM_Stream_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_MEM_Acceptor> and +// <ACE_MEM_Connector> classes. +// +// = AUTHOR +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Get_Opt.h" +#include "ace/Thread_Manager.h" +#include "ace/MEM_Connector.h" +#include "ace/MEM_Acceptor.h" +#include "ace/Select_Reactor.h" +#include "ace/Connector.h" +#include "ace/Acceptor.h" +#include "ace/Svc_Handler.h" +#include "ace/Singleton.h" +#include "ace/Atomic_Op.h" + +ACE_RCSID(tests, MEM_Stream_Test, "$Id$") + +#if (defined (ACE_HAS_THREADS) || defined (ACE_HAS_PROCESS_SPAWN)) && \ + (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) + +#if !defined (ACE_HAS_PROCESS_SPAWN) && defined (ACE_HAS_THREADS) +# define _TEST_USES_THREADS +#else +# define _TEST_USES_PROCESSES +# include "ace/Process.h" +# include "ace/Process_Manager.h" +#endif /* ACE_HAS_FORK */ + +#include "MEM_Stream_Test.h" // Defines Echo_Handler + +#define NUMBER_OF_REACTIVE_CONNECTIONS 3 +#if defined (ACE_WIN32) || !defined (_ACE_USE_SV_SEM) +# define NUMBER_OF_MT_CONNECTIONS 3 +#else + // We will use SysV Semaphore in this case which is not very scalable + // and can only handle one connection. +# define NUMBER_OF_MT_CONNECTIONS 1 +#endif /* ACE_WIN32 || !_ACE_USE_SV_SEM */ + +#define NUMBER_OF_ITERATIONS 100 + +// If we don't have winsock2 we can't use WFMO_Reactor. +#if defined (ACE_WIN32) \ + && !defined (ACE_HAS_WINCE) \ + && defined (ACE_HAS_WINSOCK2) \ + && ACE_HAS_WINSOCK2 != 0 +# define TEST_CAN_USE_WFMO_REACTOR +#endif + +#if defined (TEST_CAN_USE_WFMO_REACTOR) +static const int opt_wfmo_reactor = 1; +#endif /* TEST_CAN_USE_WFMO_REACTOR */ + +static int opt_select_reactor = 1; +static ACE_MEM_IO::Signal_Strategy client_strategy = ACE_MEM_IO::Reactive; + +typedef ACE_Atomic_Op <ACE_SYNCH_MUTEX, u_short> WaitingCounter; +typedef ACE_Singleton <WaitingCounter, ACE_SYNCH_RECURSIVE_MUTEX> Waiting; + +// Number of connections that are currently open +static u_short connection_count = 0; + +typedef ACE_Acceptor<Echo_Handler, ACE_MEM_ACCEPTOR> ACCEPTOR; +typedef ACE_Strategy_Acceptor<Echo_Handler, ACE_MEM_ACCEPTOR> S_ACCEPTOR; + +static void reset_handler (int conn) +{ + // Reset the number of connection the test should perform. + *Waiting::instance () = conn; + connection_count = 0; +} + +int +Echo_Handler::open (void *) +{ + return 0; +} + +Echo_Handler::Echo_Handler (ACE_Thread_Manager *thr_mgr) + : ACE_Svc_Handler<ACE_MEM_STREAM, ACE_SYNCH> (thr_mgr), + connection_ (++connection_count) +{ + ACE_OS::sprintf (this->name_, ACE_TEXT ("Connection %d --> "), + this->connection_); +} + +int +Echo_Handler::handle_input (ACE_HANDLE) +{ + ACE_TCHAR buf[MAXPATHLEN]; + ssize_t len; + + len = this->peer ().recv (buf, MAXPATHLEN * sizeof (ACE_TCHAR)); + + if (len == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error receiving from MEM_Stream\n")), + -1); + else if (len == 0) // Connection closed. + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Connection %d closed\n"), + this->connection_)); + return -1; + } + + ACE_TCHAR return_buf[MAXPATHLEN]; + ACE_OS::strcpy (return_buf, this->name_); + ACE_OS::strcat (return_buf, buf); + len = (ACE_OS::strlen (return_buf) + 1) * sizeof (ACE_TCHAR); + + if (this->peer ().send (return_buf, len) != len) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error sending from MEM_Stream\n")), + -1); + + return 0; +} + +int +Echo_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask mask) +{ + // Reduce count. + (*Waiting::instance ())--; + + if (client_strategy != ACE_MEM_IO::Reactive) + this->reactor ()->remove_handler (this, + mask | ACE_Event_Handler::DONT_CALL); + + // If no connections are open. + if (*Waiting::instance () == 0) + ACE_Reactor::instance ()->end_reactor_event_loop (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Echo_Handler %d::handle_close closing down\n"), + this->connection_)); + + // Shutdown + this->destroy (); + return 0; +} + +int +Echo_Handler::svc (void) +{ + while (this->handle_input (this->get_handle ()) >= 0) + continue; + return 0; +} + +static int +run_client (u_short port, + ACE_MEM_IO::Signal_Strategy strategy) +{ + int status = 0; + ACE_MEM_Addr to_server (port); + ACE_MEM_Connector connector; + connector.preferred_strategy (strategy); + ACE_MEM_Stream stream; + + // connector.preferred_strategy (ACE_MEM_IO::MT); + + if (connector.connect (stream, to_server.get_remote_addr ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), ACE_TEXT ("connector.connect()")), + -1); + + ACE_TCHAR buf[MAXPATHLEN]; + + for (ssize_t cntr = 0; cntr < NUMBER_OF_ITERATIONS; cntr ++) + { + ACE_OS::sprintf (buf, ACE_TEXT ("Iteration ")ACE_SSIZE_T_FORMAT_SPECIFIER, + cntr); + + ssize_t slen = (ACE_OS::strlen (buf) + 1) * sizeof (ACE_TCHAR); + + if (stream.send (buf, slen) < slen) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("In stream.send()"))); + status = -1; + break; + } + + if (stream.recv (buf, MAXPATHLEN) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("stream.recv()"))); + status = -1; + break; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("run_client(), got echo %s\n"), + buf)); + } + + status = stream.close () == -1 ? -1 : status; + return status; +} + +#if defined (_TEST_USES_THREADS) +static ACE_THR_FUNC_RETURN +connect_client (void *arg) +{ + u_short *sport = reinterpret_cast <u_short *> (arg); + run_client (*sport, client_strategy); + return 0; +} +#endif + +static void +create_reactor (void) +{ + ACE_Reactor_Impl *impl = 0; + +#if defined (TEST_CAN_USE_WFMO_REACTOR) + if (opt_wfmo_reactor) + ACE_NEW (impl, + ACE_WFMO_Reactor); +#endif /* TEST_CAN_USE_WFMO_REACTOR */ + + if (impl == 0 && opt_select_reactor) + ACE_NEW (impl, + ACE_Select_Reactor); + + ACE_Reactor *reactor = 0; + ACE_NEW (reactor, + ACE_Reactor (impl)); + ACE_Reactor::instance (reactor); +} + +static int +test_reactive (const ACE_TCHAR *prog, + ACE_MEM_Addr &server_addr) +{ + ACE_DEBUG ((LM_DEBUG, "Testing Reactive MEM_Stream\n\n")); + + int status = 0; + + client_strategy = ACE_MEM_IO::Reactive; // Echo_Handler uses this. + + ACE_Accept_Strategy<Echo_Handler, ACE_MEM_ACCEPTOR> accept_strategy; + ACE_Creation_Strategy<Echo_Handler> create_strategy; + ACE_Reactive_Strategy<Echo_Handler> reactive_strategy (ACE_Reactor::instance ()); + S_ACCEPTOR acceptor; + if (acceptor.open (server_addr, + ACE_Reactor::instance (), + &create_strategy, + &accept_strategy, + &reactive_strategy) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MEM_Acceptor::accept\n")), 1); + acceptor.acceptor ().mmap_prefix (ACE_TEXT ("MEM_Acceptor_")); + + ACE_MEM_Addr local_addr; + if (acceptor.acceptor ().get_local_addr (local_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MEM_Acceptor::get_local_addr\n")), + 1); + + u_short sport = local_addr.get_port_number (); + +#if defined (_TEST_USES_THREADS) + if (ACE_Thread_Manager::instance ()->spawn_n (NUMBER_OF_REACTIVE_CONNECTIONS, + connect_client, + &sport) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n ()"))); +#else + ACE_Process_Options opts; +# if defined (ACE_WIN32) || !defined (ACE_USES_WCHAR) + const ACE_TCHAR *cmdline_fmt = ACE_TEXT ("%s -p%d -r"); +# else + const ACE_TCHAR *cmdline_fmt = ACE_TEXT ("%ls -p%d -r"); +# endif /* ACE_WIN32 || !ACE_USES_WCHAR */ + opts.command_line (cmdline_fmt, prog, sport); + if (ACE_Process_Manager::instance ()->spawn_n (NUMBER_OF_REACTIVE_CONNECTIONS, + opts) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n ()"))); +#endif /* _TEST_USES_THREADS */ + + ACE_Time_Value tv (60, 0); + ACE_Reactor::instance ()->run_reactor_event_loop (tv); + + if (tv == ACE_Time_Value::zero) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Reactor::run_event_loop timeout\n"))); + status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, "Reactor::run_event_loop finished\n")); + +#if defined (_TEST_USES_THREADS) + if (ACE_Thread_Manager::instance ()->wait () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("wait ()"))); +#else + if (ACE_Process_Manager::instance ()->wait () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("wait ()"))); +#endif /* _TEST_USES_THREADS */ + + if (acceptor.close () == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("MEM_Acceptor::close\n"))); + status = 1; + } + + ACE_UNUSED_ARG (prog); + return status; +} + +static int +test_concurrent (const ACE_TCHAR *prog, + ACE_MEM_Addr &server_addr) +{ + ACE_DEBUG ((LM_DEBUG, "Testing Multithreaded MEM_Stream\n\n")); + + int status = 0; + client_strategy = ACE_MEM_IO::MT; // Echo_Handler uses this. + + ACE_Accept_Strategy<Echo_Handler, ACE_MEM_ACCEPTOR> accept_strategy; + ACE_Creation_Strategy<Echo_Handler> create_strategy; +#if defined (ACE_HAS_THREADS) + ACE_Thread_Strategy<Echo_Handler> act_strategy; +#else + ACE_Reactive_Strategy<Echo_Handler> act_strategy (ACE_Reactor::instance ()); +#endif /* ACE_HAS_THREADS */ + S_ACCEPTOR acceptor; + + if (acceptor.open (server_addr, + ACE_Reactor::instance (), + &create_strategy, + &accept_strategy, + &act_strategy) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MEM_Acceptor::accept\n")), 1); + + // Make sure the MEM_Stream created by the underlying MEM_Acceptor + // is capable of passing messages of 1MB. + acceptor.acceptor ().init_buffer_size (1024 * 1024); + acceptor.acceptor ().mmap_prefix (ACE_TEXT ("MEM_Acceptor_")); + acceptor.acceptor ().preferred_strategy (ACE_MEM_IO::MT); + + ACE_MEM_Addr local_addr; + if (acceptor.acceptor ().get_local_addr (local_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MEM_Acceptor::get_local_addr\n")), + 1); + + u_short sport = local_addr.get_port_number (); + +#if defined (_TEST_USES_THREADS) + ACE_UNUSED_ARG (prog); + + if (ACE_Thread_Manager::instance ()->spawn_n (NUMBER_OF_MT_CONNECTIONS, + connect_client, + &sport) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n()"))); +#else + ACE_Process_Options opts; +# if defined (ACE_WIN32) || !defined (ACE_USES_WCHAR) + const ACE_TCHAR *cmdline_fmt = ACE_TEXT ("%s -p%d -m"); +# else + const ACE_TCHAR *cmdline_fmt = ACE_TEXT ("%ls -p%d -m"); +# endif /* ACE_WIN32 || !ACE_USES_WCHAR */ + opts.command_line (cmdline_fmt, prog, sport); + if (ACE_Process_Manager::instance ()->spawn_n (NUMBER_OF_MT_CONNECTIONS, + opts) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n()"))); +#endif /* _TEST_USES_THREADS */ + + ACE_Time_Value tv (60, 0); + ACE_Reactor::instance ()->run_reactor_event_loop (tv); + + if (tv == ACE_Time_Value::zero) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Reactor::run_event_loop timeout\n"))); + status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, "Reactor::run_event_loop finished\n")); + +#if defined (_TEST_USES_THREADS) + if (ACE_Thread_Manager::instance ()->wait () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("wait ()"))); +#else + if (ACE_Process_Manager::instance ()->wait () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("wait ()"))); +#endif /* _TEST_USES_THREADS */ + + if (acceptor.close () == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("MEM_Acceptor::close"))); + status = 1; + } + + return status; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + u_short port = 0; + + if (argc == 1) + { + // This is the "master" process. + + ACE_START_TEST (ACE_TEXT ("MEM_Stream_Test")); + create_reactor (); + ACE_MEM_Addr server_addr (port); + + reset_handler (NUMBER_OF_REACTIVE_CONNECTIONS); + +#if defined (ACE_HAS_WINCE) + test_reactive (ACE_TEXT("\\Windows\\Start Menu\\MEM_Stream_Test_WinCE.exe"), server_addr); +#else + test_reactive (ACE_TEXT ("MEM_Stream_Test"), server_addr); +#endif // ACE_HAS_WINCE + + ACE_Reactor::instance ()->reset_reactor_event_loop (); + +#if !defined (ACE_WIN32) && defined (_ACE_USE_SV_SEM) + ACE_ERROR ((LM_DEBUG, + ACE_TEXT ("\n *** Platform only supports non-scalable SysV semaphores ***\n\n"))); +#endif /* !ACE_WIN32 && _ACE_USE_SV_SEM */ + reset_handler (NUMBER_OF_MT_CONNECTIONS); + +#if defined (ACE_HAS_WINCE) + test_concurrent (ACE_TEXT("\\Windows\\Start Menu\\MEM_Stream_Test_WinCE.exe"), server_addr); +#else + test_concurrent (ACE_TEXT ("MEM_Stream_Test"), server_addr); +#endif // ACE_HAS_WINCE + + ACE_END_TEST; + return 0; + } + else + { + // We end up here if this is a child process spawned for one of + // the test passes. command line is: -p <port> -r (reactive) | + // -m (multithreaded) + + ACE_TCHAR lognm[MAXPATHLEN]; + int mypid (ACE_OS::getpid ()); + ACE_OS::sprintf(lognm, ACE_TEXT ("MEM_Stream_Test-%d"), mypid); + ACE_START_TEST (lognm); + + ACE_Get_Opt opts (argc, argv, ACE_TEXT ("p:rm")); + int opt, iport, status; + ACE_MEM_IO::Signal_Strategy model = ACE_MEM_IO::Reactive; + + while ((opt = opts()) != -1) + { + switch (opt) + { + case 'p': + iport = ACE_OS::atoi (opts.opt_arg ()); + port = static_cast <u_short> (iport); + break; + + case 'r': + model = ACE_MEM_IO::Reactive; + break; + + case 'm': + model = ACE_MEM_IO::MT; + break; + + default: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Invalid option (-p <port> -r | -m)\n")), + 1); + } + } + status = run_client (port, model); + ACE_END_TEST; + return status; + } +} + +#if defined (ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION) + +template ACE_Singleton<ACE_Atomic_Op<ACE_SYNCH_MUTEX, u_short>, + ACE_SYNCH_RECURSIVE_MUTEX> * + ACE_Singleton<ACE_Atomic_Op<ACE_SYNCH_MUTEX, u_short>, + ACE_SYNCH_RECURSIVE_MUTEX>::singleton_; + +#endif /* ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION */ + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("MEM_Stream_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("position independent pointers ") + ACE_TEXT ("not supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} +#endif /* (ACE_HAS_THREADS || ACE_HAS_PROCESS_SPAWN) && ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1 */ diff --git a/ACE/tests/MEM_Stream_Test.h b/ACE/tests/MEM_Stream_Test.h new file mode 100644 index 00000000000..b9ed6924463 --- /dev/null +++ b/ACE/tests/MEM_Stream_Test.h @@ -0,0 +1,57 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MEM_Stream_Test.h +// +// = DESCRIPTION +// This file has the class definitions needed for template generation in +// MEM_Stream_Test.cpp. They have to be in a separate file so AIX xlC can +// find them at auto-instantiate time. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_MEM_STREAM_TEST_H +#define ACE_TESTS_MEM_STREAM_TEST_H + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/MEM_Stream.h" +#include "ace/Reactor.h" +#include "ace/Svc_Handler.h" +#include "ace/Synch_Traits.h" + +#if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) + +class Echo_Handler : public ACE_Svc_Handler<ACE_MEM_STREAM, ACE_SYNCH> +{ + // = TITLE + // Simple class for reading in the data and then sending it back +public: + Echo_Handler (ACE_Thread_Manager *thr_mgr = 0); + virtual int open (void *); + static void reset_handler (void); + virtual int handle_input (ACE_HANDLE h); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + // The Svc_Handler callbacks. + virtual int svc (void); + +private: + ACE_TCHAR name_[MAXPATHLEN]; + u_short connection_; +}; + +#endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS */ + +#endif /* ACE_TESTS_MEM_STREAM_TEST_H */ diff --git a/ACE/tests/MM_Shared_Memory_Test.cpp b/ACE/tests/MM_Shared_Memory_Test.cpp new file mode 100644 index 00000000000..557c9c38fe5 --- /dev/null +++ b/ACE/tests/MM_Shared_Memory_Test.cpp @@ -0,0 +1,232 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Shared_Memory_MM_Test.cpp +// +// = DESCRIPTION +// This is a simple test of <ACE_Shared_Memory_MM>. The test +// forks two processes or spawns two threads (depending upon the +// platform) and then executes child and parent allowing them to +// exchange data using shared memory. No user input is required as +// far as command line arguments are concerned. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// and Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Shared_Memory_MM.h" +#include "ace/SV_Semaphore_Simple.h" +#include "ace/Process_Semaphore.h" +#include "ace/Thread_Manager.h" +#include "ace/ACE.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, MM_Shared_Memory_Test, "$Id$") + +#if !defined (ACE_LACKS_MMAP) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; +const int SHMSZ = 27; +static ACE_TCHAR *shm_key; + +#if defined (ACE_LACKS_FORK) +#include "ace/Thread_Semaphore.h" +typedef ACE_Thread_Semaphore SYNCHRONIZER; +#elif defined (ACE_HAS_POSIX_SEM) && defined(ACE_HAS_SYSV_IPC) +class SYNCHRONIZER : public ACE_SV_Semaphore_Simple +{ + // = TITLE + // If the platform has native cross-process POSIX semaphores, we + // must *force* this test to use the System V Semaphores in order + // to get the right semantics. +public: + SYNCHRONIZER (int initial_value) + : ACE_SV_Semaphore_Simple ((const char *) 0, + ACE_SV_Semaphore_Simple::ACE_CREATE, + initial_value) + {} +}; +#else +typedef ACE_Process_Semaphore SYNCHRONIZER; +#endif /* !defined (ACE_LACKS_FORK) */ + +// Synchronize the start of the parent and the child. +static SYNCHRONIZER *synchronizer = 0; + +static void * +child (void * = 0) +{ + int result; + + // Wait for the parent to be initialized. + result = synchronizer->acquire (); + ACE_ASSERT (result != -1); + + const char *t = ACE_ALPHABET; + ACE_Shared_Memory_MM shm_child; + + result = shm_child.open (shm_key); + ACE_ASSERT (result != -1); + + char *shm = (char *) shm_child.malloc (); + + ACE_ASSERT (shm != 0); + + for (char *s = shm; *s != '\0'; s++) + { + ACE_ASSERT (*t == s[0]); + t++; + } + + // Indicate to the parent that we're done. + *shm = '*'; + + return 0; +} + +static void * +parent (void * = 0) +{ + int result; + ACE_Shared_Memory_MM shm_parent; + + result = shm_parent.open (shm_key, SHMSZ); + ACE_ASSERT (result != -1); + + char *shm = (char *) shm_parent.malloc (); + + ACE_ASSERT (shm != 0); + + char *s = shm; + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + *s++ = *c; + + *s = '\0'; + + // Allow the child to proceed. + result = synchronizer->release (); + ACE_ASSERT (result != -1); + + // Perform a "busy wait" until the child sets the character to '*'. + while (*shm != '*') + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) spinning in parent!\n"))); + + result = shm_parent.remove (); + ACE_ASSERT (result != -1); + + ACE_OS::unlink (shm_key); + return 0; +} + +static int +spawn (void) +{ + // Create the synchronizer before spawning the child process/thread, + // to avoid race condition between the creation in the parent and + // use in the child. + ACE_NEW_RETURN (synchronizer, + SYNCHRONIZER ((unsigned int)0), // Locked by default... + -1); + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("fork failed")), + 1); + /* NOTREACHED */ + case 0: + parent (); + // Remove the semaphore. + synchronizer->remove (); + delete synchronizer; + break; + /* NOTREACHED */ + default: + child (); + delete synchronizer; + break; + /* NOTREACHED */ + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (child), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("thread create failed")), + 1); + else if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (parent), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("thread create failed")), + 1); + ACE_Thread_Manager::instance ()->wait (); + delete synchronizer; +#else + ACE_UNUSED_ARG (synchronizer); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("only one thread may be run in a process on this platform\n")), + 1); +#endif /* ACE_HAS_THREADS */ + return 0; +} +#endif /* !ACE_LACKS_MMAP */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("MM_Shared_Memory_Test")); + +#if !defined (ACE_LACKS_MMAP) + ACE_TCHAR temp_file[MAXPATHLEN + 1]; + + // Get the temporary directory, + // The - 24 is for the filename, mm_shared_mem_testXXXXXX + if (ACE::get_temp_dir (temp_file, MAXPATHLEN - 24) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Temporary path too long\n")), -1); + + // Add the filename to the end + ACE_OS::strcat (temp_file, ACE_TEXT ("mm_shared_mem_testXXXXXX")); + + // Store in the global variable. + shm_key = temp_file; + + if (ACE_OS::mktemp (shm_key) == 0 + || (ACE_OS::unlink (shm_key) == -1 +#ifndef ACE_HAS_WINCE + && errno == EPERM +#endif + )) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + shm_key), + 1); + spawn (); + +#else /* !ACE_LACKS_MMAP */ + ACE_ERROR ((LM_INFO, + ACE_TEXT ("mmap ") + ACE_TEXT ("is not supported on this platform\n"))); +#endif /* !ACE_LACKS_MMAP */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/MT_Reactor_Timer_Test.cpp b/ACE/tests/MT_Reactor_Timer_Test.cpp new file mode 100644 index 00000000000..5a714be719b --- /dev/null +++ b/ACE/tests/MT_Reactor_Timer_Test.cpp @@ -0,0 +1,374 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MT_Reactor_Timer_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the timer mechanism of +// the reactor scheduling timers, handling expired timers and +// cancelling scheduled timers from multiple threads. No command +// line arguments are needed to run the test. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" +#include "MT_Reactor_Timer_Test.h" +#include "ace/ACE.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, MT_Reactor_Timer_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +// This test exercises the setting and cancelling of timers from a +// thread other than the one the reactor is running in. It sets up an +// initial set of timers (3, 4, 5 seconds) from the main thread. When +// the second thread starts, it cancels the 3 second timer and sets a +// 2-second timer and an already-expired timer, which should be the +// first to fire. It then sleeps for 3 seconds (letting the 2 second +// timer fire, and if things are slow, the 4 second timer will also +// fire. Then it sets 2 more timers at 10 and 12 seconds and cancels +// the original 5 second timer. Then returns, ending the thread. The +// destructor for Time_Handler insures that everything happened +// correctly. + +Time_Handler::Time_Handler (void) +{ + for (int i = 0; + i < Time_Handler::TIMER_SLOTS; + this->timer_id_[i++] = Time_Handler::TIMER_NOTSET) + continue; + + this->prev_timer_ = -1; +} + +// Set up initial timer conditions. Timers set up at 3, 4, and 5 +// seconds. The one at 3 seconds will get cancelled when the thread +// starts. + +void +Time_Handler::setup (void) +{ + ACE_Reactor *r = ACE_Reactor::instance (); + + this->timer_id_[2] = r->schedule_timer (this, + (const void *) 2, + ACE_Time_Value (3)); + this->timer_id_[3] = r->schedule_timer (this, + (const void *) 3, + ACE_Time_Value (4)); + this->timer_id_[4] = r->schedule_timer (this, + (const void *) 4, + ACE_Time_Value (5)); + return; +} + +int +Time_Handler::verify_results (void) +{ + ACE_ASSERT (this->timer_id_[0] == Time_Handler::TIMER_FIRED); + ACE_ASSERT (this->timer_id_[1] == Time_Handler::TIMER_FIRED); + ACE_ASSERT (this->timer_id_[2] == Time_Handler::TIMER_CANCELLED); + ACE_ASSERT (this->timer_id_[3] == Time_Handler::TIMER_FIRED); + ACE_ASSERT (this->timer_id_[4] == Time_Handler::TIMER_CANCELLED); + ACE_ASSERT (this->timer_id_[5] == Time_Handler::TIMER_FIRED); + ACE_ASSERT (this->timer_id_[6] == Time_Handler::TIMER_FIRED); + + for (int i = 7; i < Time_Handler::TIMER_SLOTS; i++) + ACE_ASSERT (this->timer_id_[i] == Time_Handler::TIMER_NOTSET); + + return 0; +} + +int +Time_Handler::svc (void) +{ + ACE_Reactor *r = ACE_Reactor::instance (); + + ACE_ASSERT (r->cancel_timer (this->timer_id_[2]) == 1); + this->timer_id_[2] = Time_Handler::TIMER_CANCELLED; + + this->timer_id_[1] = r->schedule_timer(this, + (const void *) 1, + ACE_Time_Value (2)); + // This one may get the callback before we return, so serialize. + this->lock_.acquire (); + this->timer_id_[0] = r->schedule_timer(this, + (const void *) 0, + ACE_Time_Value (0, -5)); + this->lock_.release (); + ACE_OS::sleep(3); + + this->timer_id_[5] = r->schedule_timer(this, + (const void *)5, + ACE_Time_Value (10)); + this->timer_id_[6] = r->schedule_timer(this, + (const void *)6, + ACE_Time_Value (12)); + + ACE_ASSERT (r->cancel_timer (this->timer_id_[4]) == 1); + this->timer_id_[4] = Time_Handler::TIMER_CANCELLED; + + return 0; +} + +int +Time_Handler::handle_timeout (const ACE_Time_Value &tv, + const void *arg) +{ + long time_tag = static_cast <long> (reinterpret_cast <size_t> (arg)); + ACE_UNUSED_ARG(tv); + + ACE_GUARD_RETURN (ACE_Thread_Mutex, id_lock, this->lock_, 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%T (%t): Timer #%d (id #%d) expired\n"), + time_tag, + this->timer_id_[time_tag])); + + ACE_ASSERT (time_tag > this->prev_timer_); + ACE_ASSERT (this->timer_id_[time_tag] != Time_Handler::TIMER_NOTSET); + ACE_ASSERT (this->timer_id_[time_tag] != Time_Handler::TIMER_CANCELLED); + ACE_ASSERT (this->timer_id_[time_tag] != Time_Handler::TIMER_FIRED); + this->timer_id_[time_tag] = Time_Handler::TIMER_FIRED; + this->prev_timer_ = time_tag; + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +Dispatch_Count_Handler::Dispatch_Count_Handler (void) +{ + + ACE_Reactor *r = ACE_Reactor::instance (); + + this->input_seen_ = this->notify_seen_ = 0; + this->timers_fired_ = 0; + + // Initialize the pipe. + if (this->pipe_.open () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_Pipe::open"))); + // Register the "read" end of the pipe. + else if (r->register_handler (this->pipe_.read_handle (), + this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("register_handler"))); + // Put something in our pipe and smoke it... ;-) + else if (ACE::send (this->pipe_.write_handle (), + "z", + 1) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send"))); + // Call notify to prime the pump for this, as well. + else if (r->notify (this) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("notify"))); +} + +int +Dispatch_Count_Handler::handle_close (ACE_HANDLE h, + ACE_Reactor_Mask m) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%T (%t): handle_close\n"))); + + ACE_ASSERT (h == this->pipe_.read_handle () + && m == ACE_Event_Handler::READ_MASK); + + return 0; +} + +int +Dispatch_Count_Handler::handle_input (ACE_HANDLE h) +{ + char c; + + ACE_ASSERT (this->input_seen_ == 0); + this->input_seen_ = 1; + + if (ACE::recv (h, &c, 1) != 1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv")), + -1); + + ACE_ASSERT (c == 'z'); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%T (%t): handle_input\n"))); + // Trigger the <handle_close> hook. + return -1; +} + +int +Dispatch_Count_Handler::handle_exception (ACE_HANDLE h) +{ + ACE_UNUSED_ARG (h); + + ACE_ASSERT (this->notify_seen_ == 0); + this->notify_seen_ = 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%T (%t): handle_exception\n"))); + return 0; +} + +int +Dispatch_Count_Handler::handle_timeout (const ACE_Time_Value &tv, + const void *arg) +{ + ACE_UNUSED_ARG (tv); + + ++this->timers_fired_; + + long value = static_cast <long> (reinterpret_cast <size_t> (arg)); + + // This case just tests to make sure the Reactor is counting timer + // expiration correctly. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%T (%t): expiration %d\n"), + value)); + return 0; +} + +int +Dispatch_Count_Handler::verify_results (void) +{ + int result = 0; + + if (this->input_seen_ != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("input_seen_ is not 1 but %d\n"), + input_seen_)); + result = -1; + } + + if (this->notify_seen_ != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("notify_seen_ is not 1 but %d\n"), + notify_seen_)); + result = -1; + } + + if (this->timers_fired_ != ACE_MAX_TIMERS) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("timers fired not equal max timers: %d != %d\n"), + this->timers_fired_, + ACE_MAX_TIMERS)); + result = -1; + } + + return result; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_Reactor_Timer_Test")); + + int status = 0; + int test_result = 0; + + ACE_Reactor *r = ACE_Reactor::instance (); + + Dispatch_Count_Handler callback; + + for (int i = ACE_MAX_TIMERS; i > 0; i--) + // Schedule a timeout to expire immediately. + if (r->schedule_timer (&callback, + reinterpret_cast <const void *> (static_cast <size_t> (i)), + ACE_Time_Value (0)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("schedule_timer")), + 1); + + ACE_Time_Value no_waiting (0); + size_t events = 0; + + while (1) + { + int result = r->handle_events (no_waiting); + + // Timeout. + if (result == 0) + break; + + // Make sure there were no errors. + ACE_ASSERT (result != -1); + + events += result; + } + + // All <ACE_MAX_TIMERS> + 2 I/O dispatches (one for <handle_input> + // and the other for <handle_exception>) should be counted in + // events. + if (events < ACE_MAX_TIMERS + 2) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("expected %d events, got %d instead\n"), + ACE_MAX_TIMERS + 2, + events)); + } + + status = callback.verify_results (); + if (status != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Dispatch counting test failed.\n"))); + test_result = 1; + } + +#if defined (ACE_HAS_THREADS) + + Time_Handler other_thread; + ACE_Time_Value time_limit (30); + + // Set up initial set of timers. + other_thread.setup (); + + other_thread.activate (THR_NEW_LWP | THR_JOINABLE); + status = ACE_Reactor::instance()->run_reactor_event_loop (time_limit); + // Should have returned only because the time limit is up... + ACE_ASSERT (status != -1); + ACE_ASSERT (time_limit.sec () == 0); + + status = other_thread.wait (); + if (status == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p, errno is %d\n"), + "wait ()", + errno)); + ACE_ASSERT (status != -1); + } + + status = other_thread.verify_results (); + if (status != 0) + test_result = 1; + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return test_result; +} diff --git a/ACE/tests/MT_Reactor_Timer_Test.h b/ACE/tests/MT_Reactor_Timer_Test.h new file mode 100644 index 00000000000..d3dce675ed4 --- /dev/null +++ b/ACE/tests/MT_Reactor_Timer_Test.h @@ -0,0 +1,108 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MT_Reactor_Timer_Test.h +// +// = DESCRIPTION +// This file contains class definitions needed for template +// instantiation in the MT_Reactor_Timer_Test.cpp file. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_MT_REACTOR_TIMER_TEST_H +#define ACE_TESTS_MT_REACTOR_TIMER_TEST_H + +#include "ace/Reactor.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Task.h" +#include "ace/Pipe.h" + +class Time_Handler : public ACE_Task<ACE_SYNCH> +{ + // = TITLE + // Test out the multi-threading features of the Reactor's timer + // mechanism. +public: + Time_Handler (void); + + void setup (void); + + int verify_results(void); + + virtual int svc (void); + // Run by a daemon thread to handle deferred processing. + + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg); + +private: + enum + { + TIMER_SLOTS = 10 + }; + + long timer_id_[TIMER_SLOTS]; + // The timer_id_ array holds timer IDs. They also have some other values + // that are specific to this test: + // -1 the timer has not been set + // -2 the timer was set, but has been cancelled + // -3 the timer was set, and it already fired + enum { TIMER_NOTSET = -1, TIMER_CANCELLED = -2, TIMER_FIRED = -3 }; + + long prev_timer_; + +#if defined ACE_HAS_THREADS + ACE_Thread_Mutex lock_; +#endif /* ACE_HAS_THREADS */ + +}; + +class Dispatch_Count_Handler : public ACE_Event_Handler +{ + // = TITLE + // A simple test to ensure that the Reactor counts the number of + // dispatches correctly. +public: + Dispatch_Count_Handler (void); + + int handle_close (ACE_HANDLE h, + ACE_Reactor_Mask m); + // Clean up resources from the Reactor. + + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg); + // Keep track of the number of timeouts. + + virtual int handle_input (ACE_HANDLE); + // Keep track of the number of I/O events. + + virtual int handle_exception (ACE_HANDLE); + // Keep track of the number of notifies. + + int verify_results (void); + // Verify that the expected events did happen. + +private: + ACE_Pipe pipe_; + // Provide something to trigger I/O. + + int input_seen_; + int notify_seen_; + size_t timers_fired_; + +}; + +#endif /* ACE_TESTS_MT_REACTOR_TIMER_TEST_H */ diff --git a/ACE/tests/MT_Reactor_Upcall_Test.cpp b/ACE/tests/MT_Reactor_Upcall_Test.cpp new file mode 100644 index 00000000000..e446707dbdc --- /dev/null +++ b/ACE/tests/MT_Reactor_Upcall_Test.cpp @@ -0,0 +1,384 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MT_Reactor_Upcall_Test.cpp +// +// = DESCRIPTION +// This is a test that shows how to handle upcalls from the +// TP_Reactor and the WFMO_Reactor when the event loop is being run +// by multiple threads. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Dev_Poll_Reactor.h" +#include "ace/Pipe.h" +#include "ace/Task.h" +#include "ace/Get_Opt.h" +#include "ace/ACE.h" + +ACE_RCSID(tests, MT_Reactor_Upcall_Test, "$Id$") + +int number_of_event_loop_threads = 3; +int number_of_messages = 10; +int sleep_time_in_msec = 100; +int lock_upcall = 1; +static const char *message = "Hello there!"; + +class Guard +{ +public: + Guard (ACE_SYNCH_MUTEX &lock) + : lock_ (lock) + { + if (lock_upcall) + lock.acquire (); + } + + ~Guard (void) + { + if (lock_upcall) + this->lock_.release (); + } + + ACE_SYNCH_MUTEX &lock_; +}; + +struct Message +{ + enum Type + { + DATA, + SHUTDOWN + }; + + Type type_; + size_t size_; + char data_[BUFSIZ]; +}; + +class Handler : public ACE_Event_Handler +{ +public: + Handler (ACE_Reactor &reactor); + int handle_input (ACE_HANDLE fd); + + ACE_Pipe pipe_; + int number_of_messages_read_; + ACE_SYNCH_MUTEX lock_; + int shutdown_; +}; + +Handler::Handler (ACE_Reactor &reactor) + : ACE_Event_Handler (&reactor), + number_of_messages_read_ (0), + shutdown_ (1) +{ + // Create the pipe. + int result = this->pipe_.open (); + if (result != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("pipe open"))); + else + { + // Register for input events. + result = + this->reactor ()->register_handler (this->pipe_.read_handle (), + this, + ACE_Event_Handler::READ_MASK); + if (result != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Can't register pipe for READ"))); + else + this->shutdown_ = 0; + } +} + +int +Handler::handle_input (ACE_HANDLE fd) +{ + Guard monitor (this->lock_); + + // If we have been shutdown, return. + if (this->shutdown_) + return 0; + + // Read fixed parts of message. + Message message; + ssize_t result = + ACE::recv_n (fd, + &message.type_, + sizeof (message.type_)); + if (result != static_cast<ssize_t> (sizeof (message.type_))) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): read %d, %p\n"), + result, + ACE_TEXT ("recv 1"))); + result = + ACE::recv_n (fd, + &message.size_, + sizeof (message.size_)); + if (result != static_cast<ssize_t> (sizeof (message.size_))) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): read %d, %p\n"), + result, + ACE_TEXT ("recv 2"))); + + // On shutdown message, stop the event loop. + if (message.type_ == Message::SHUTDOWN) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Shutdown message\n"))); + + this->shutdown_ = 1; + + this->reactor ()->end_reactor_event_loop (); + + // Remove self from Reactor. + return -1; + } + + // Else it is a data message: read the data. + result = + ACE::recv_n (fd, + &message.data_, + message.size_); + if (result != static_cast<ssize_t> (message.size_)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): read %d, %p\n"), + result, + ACE_TEXT ("recv 3"))); + else + { + message.data_[result] = '\0'; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Starting to handle message %d: %s\n"), + this->number_of_messages_read_ + 1, + message.data_)); + } + + // Process message (sleep). + ACE_OS::sleep (ACE_Time_Value (0, + sleep_time_in_msec * 1000)); + + // Keep count. + this->number_of_messages_read_++; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Completed handling message %d\n"), + this->number_of_messages_read_)); + + return 0; +} + +class Event_Loop_Task : public ACE_Task_Base +{ +public: + Event_Loop_Task (ACE_Reactor &reactor); + int svc (void); + +private: + ACE_Reactor &reactor_; +}; + +Event_Loop_Task::Event_Loop_Task (ACE_Reactor &reactor) + : reactor_ (reactor) +{ +} + +int +Event_Loop_Task::svc (void) +{ + return this->reactor_.run_reactor_event_loop (); +} + +void +test_reactor_upcall (ACE_Reactor &reactor) +{ + Handler handler (reactor); + if (handler.shutdown_) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Error initializing test; abort.\n"))); + return; + } + + Event_Loop_Task event_loop_task (reactor); + + // Start up the event loop threads. + int result = + event_loop_task.activate (THR_NEW_LWP | THR_JOINABLE, + number_of_event_loop_threads); + if (result != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test_reactor_upcall, activate"))); + + // Data message. + Message data_message; + data_message.type_ = + Message::DATA; + data_message.size_ = + ACE_OS::strlen (message); + ACE_OS::strcpy (data_message.data_, message); + + // Send in three pieces because the struct members may not be adjacent + // in memory. + for (int i = 0; + i < number_of_messages; + ++i) + { + // This should trigger a call to <handle_input>. + ssize_t sent = + ACE::send_n (handler.pipe_.write_handle (), + &data_message.type_, + sizeof (data_message.type_)); + if (sent == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): %p\n"), ACE_TEXT ("send 1"))); + sent = + ACE::send_n (handler.pipe_.write_handle (), + &data_message.size_, + sizeof (data_message.size_)); + if (sent == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): %p\n"), ACE_TEXT ("send 2"))); + sent = + ACE::send_n (handler.pipe_.write_handle (), + &data_message.data_, + data_message.size_); + if (sent == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): %p\n"), ACE_TEXT ("send 3"))); + } + + // We are done: send shutdown message. + Message shutdown_message; + shutdown_message.type_ = + Message::SHUTDOWN; + shutdown_message.size_ = 0; + + // This should trigger a call to <handle_input>. + ssize_t sent = ACE::send_n (handler.pipe_.write_handle (), + &shutdown_message.type_, + sizeof (shutdown_message.type_)); + if (sent == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): %p\n"), ACE_TEXT ("send 4"))); + sent = ACE::send_n (handler.pipe_.write_handle (), + &shutdown_message.size_, + sizeof (shutdown_message.size_)); + if (sent == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t): %p\n"), ACE_TEXT ("send 5"))); + + // Wait for the event loop tasks to exit. + event_loop_task.wait (); +} + +int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("t:m:s:l:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 't': + number_of_event_loop_threads = + ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'm': + number_of_messages = + ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 's': + sleep_time_in_msec = + ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'l': + lock_upcall = + ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("usage: %s\n") + ACE_TEXT ("\t-m <number of messages> (defaults to %d)\n") + ACE_TEXT ("\t-t <number of event loop threads> (defaults to %d)\n") + ACE_TEXT ("\t-s <sleep time in msec> (defaults to %d)\n") + ACE_TEXT ("\t-l <lock upcall> (defaults to %d)\n") + ACE_TEXT ("\n"), + argv [0], + number_of_messages, + number_of_event_loop_threads, + sleep_time_in_msec, + lock_upcall), + -1); + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_Reactor_Upcall_Test")); + +#if defined (ACE_HAS_THREADS) + + // ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + int result = + parse_args (argc, argv); + + if (result != 0) + return result; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing TP Reactor\n"))); + + ACE_TP_Reactor tp_reactor_impl; + ACE_Reactor tp_reactor (&tp_reactor_impl); + + test_reactor_upcall (tp_reactor); + +#if defined (ACE_HAS_EVENT_POLL) + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing Dev Poll Reactor\n"))); + + ACE_Dev_Poll_Reactor dev_poll_reactor_impl; + ACE_Reactor dev_poll_reactor (&dev_poll_reactor_impl); + + test_reactor_upcall (dev_poll_reactor); + +#endif /* ACE_HAS_EVENT_POLL */ + +#if defined (ACE_WIN32) && \ + (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0)) + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing WFMO Reactor\n"))); + + ACE_WFMO_Reactor wfmo_reactor_impl; + ACE_Reactor wfmo_reactor (&wfmo_reactor_impl); + + test_reactor_upcall (wfmo_reactor); + +#endif /* ACE_WIN32 && ACE_HAS_WINSOCK2 */ + +#else /* ACE_HAS_THREADS */ + ACE_UNUSED_ARG(argc); + ACE_UNUSED_ARG(argv); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); + +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/MT_Reference_Counted_Event_Handler_Test.cpp b/ACE/tests/MT_Reference_Counted_Event_Handler_Test.cpp new file mode 100644 index 00000000000..45bd1924a92 --- /dev/null +++ b/ACE/tests/MT_Reference_Counted_Event_Handler_Test.cpp @@ -0,0 +1,1477 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MT_Reference_Counted_Event_Handler_Test.cpp +// +// = DESCRIPTION +// +// This test tries to represents what happens in the ORB wrt to +// event handlers, reactors, timer queues, threads, and connection +// caches, minus the other complexities. The following reactors +// are tested: Select, TP, WFMO, and Dev Poll (if enabled). +// +// The test checks proper use and shutting down of client-side +// event handlers when it is used by invocation threads and/or +// event loop threads. Server-side event handlers are either +// threaded or reactive. A purger thread is introduced to check the +// connection recycling and cache purging. Nested upcalls are also +// tested. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Dev_Poll_Reactor.h" +#include "ace/Get_Opt.h" +#include "ace/Task.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Auto_Event.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_sys_socket.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, MT_Reference_Counted_Event_Handler_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +static const char message[] = "abcdefghijklmnopqrstuvwxyz"; +static const int message_size = 26; +static int test_select_reactor = 1; +static int test_tp_reactor = 1; +static int test_wfmo_reactor = 1; +static int test_dev_poll_reactor = 1; +static int debug = 0; +static int number_of_connections = 5; +static int max_nested_upcall_level = 10; +static int close_timeout = 500; +static int pipe_open_attempts = 10; +static int pipe_retry_timeout = 1; +static int make_invocations = -1; +static int run_event_loop_thread = -1; +static int run_purger_thread = -1; +static int run_receiver_thread = -1; +static int nested_upcalls = -1; + +static ACE_HANDLE server_handle = ACE_INVALID_HANDLE; +static ACE_HANDLE client_handle = ACE_INVALID_HANDLE; + +static int number_of_options = 5; +static int test_configs[][5] = + { + // + // make_invocations, run_event_loop_thread, run_purger_thread, run_receiver_thread, nested_upcalls + // + + // { 0, 0, 0, 0, 0, }, // At least one thread should be running. + // { 0, 0, 0, 1, 0, }, // If event_loop_thread is not running and invocation_thread is not making invocations, + // no thread will know that the socket is closed. + // { 0, 0, 1, 0, 0, }, // If invocation_thread is not making invocations and if receiver is not threaded, + // we cannot decide which socket to close. + // { 0, 0, 1, 1, 0, }, // If event_loop_thread is not running and invocation_thread is not making invocations, + // no thread will know that the socket is closed. + // { 0, 1, 0, 0, 0, }, // If invocation_thread is not making invocations and if receiver is not threaded, + // we cannot decide which socket to close. + { 0, 1, 0, 1, 0, }, + // { 0, 1, 0, 1, 1, }, // No need for nested upcalls without invocations. + // { 0, 1, 1, 0, 0, }, // If invocation_thread is not making invocations and if receiver is not threaded, + // we cannot decide which socket to close. + { 0, 1, 1, 1, 0, }, + // { 0, 1, 1, 1, 1, }, // No need for nested upcalls without invocations. + // { 1, 0, 0, 0, 0, }, // If both event_loop_thread and receiver are not threaded, + // no thread can receive the messages. + { 1, 0, 0, 1, 0, }, + // { 1, 0, 0, 1, 1, }, // No need for nested upcalls without event loop being used by the receiver. + // { 1, 0, 1, 0, 0, }, // If both event_loop_thread and receiver are not threaded, + // no thread can receive the messages. + { 1, 0, 1, 1, 0, }, + // { 1, 0, 1, 1, 1, }, // No need for nested upcalls without event loop being used by the receiver. + { 1, 1, 0, 0, 0, }, + { 1, 1, 0, 0, 1, }, + { 1, 1, 0, 1, 0, }, + // { 1, 1, 0, 1, 1, }, // No need for nested upcalls without event loop being used by the receiver. + { 1, 1, 1, 0, 0, }, + { 1, 1, 1, 0, 1, }, + { 1, 1, 1, 1, 0, }, + // { 1, 1, 1, 1, 1, }, // No need for nested upcalls without event loop being used by the receiver. + }; + +/* Replication of the ACE_Pipe class. Only difference is that this + class always uses two sockets to create the pipe, even on platforms + that support pipes. */ + +class Pipe +{ +public: + + Pipe (void); + + //FUZZ: disable check_for_lack_ACE_OS + int open (void); + //FUZZ: enable check_for_lack_ACE_OS + + ACE_HANDLE read_handle (void) const; + + ACE_HANDLE write_handle (void) const; + +private: + ACE_HANDLE handles_[2]; +}; + +int +Pipe::open (void) +{ + ACE_INET_Addr my_addr; + ACE_SOCK_Acceptor acceptor; + ACE_SOCK_Connector connector; + ACE_SOCK_Stream reader; + ACE_SOCK_Stream writer; + int result = 0; + + // Bind listener to any port and then find out what the port was. + if (acceptor.open (ACE_Addr::sap_any) == -1 + || acceptor.get_local_addr (my_addr) == -1) + result = -1; + else + { + int af = my_addr.get_type (); + const ACE_TCHAR *local = ACE_LOCALHOST; +#if defined (ACE_HAS_IPV6) + if (af == AF_INET6) + local = ACE_IPV6_LOCALHOST; +#endif /* ACE_HAS_IPV6 */ + ACE_INET_Addr sv_addr (my_addr.get_port_number (), + local, + af); + + // Establish a connection within the same process. + if (connector.connect (writer, sv_addr) == -1) + result = -1; + else if (acceptor.accept (reader) == -1) + { + writer.close (); + result = -1; + } + } + + // Close down the acceptor endpoint since we don't need it anymore. + acceptor.close (); + if (result == -1) + return -1; + + this->handles_[0] = reader.get_handle (); + this->handles_[1] = writer.get_handle (); + + return 0; +} + +Pipe::Pipe (void) +{ + this->handles_[0] = ACE_INVALID_HANDLE; + this->handles_[1] = ACE_INVALID_HANDLE; +} + +ACE_HANDLE +Pipe::read_handle (void) const +{ + return this->handles_[0]; +} + +ACE_HANDLE +Pipe::write_handle (void) const +{ + return this->handles_[1]; +} + +class Connection_Cache; +class Event_Loop_Thread; + +static Event_Loop_Thread *global_event_loop_thread_variable = 0; + +class Sender : public ACE_Event_Handler +{ +public: + + Sender (ACE_HANDLE handle, + Connection_Cache &connection_cache); + + ~Sender (void); + + int handle_input (ACE_HANDLE); + + ssize_t send_message (void); + + //FUZZ: disable check_for_lack_ACE_OS + void close (void); + //FUZZ: enable check_for_lack_ACE_OS + + ACE_HANDLE handle_; + + Connection_Cache &connection_cache_; + +}; + +class Connection_Cache +{ +public: + + Connection_Cache (void); + + ~Connection_Cache (void); + + void add_connection (Sender *sender); + + void remove_connection (Sender *sender); + + Sender *acquire_connection (void); + + void release_connection (Sender *sender); + + int find (Sender *sender); + + ACE_SYNCH_MUTEX &lock (void); + + enum State + { + IDLE, + BUSY, + NOT_IN_CACHE + }; + + struct Entry + { + Sender *sender_; + State state_; + }; + + Entry *entries_; + + ACE_SYNCH_MUTEX lock_; +}; + +Sender::Sender (ACE_HANDLE handle, + Connection_Cache &connection_cache) + : handle_ (handle), + connection_cache_ (connection_cache) +{ + // Enable reference counting. + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Reference count in Sender::Sender() is %d\n"), + this->reference_count_.value ())); +} + +Sender::~Sender (void) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Reference count in ~Sender::Sender() is %d\n"), + this->reference_count_.value ())); + + // Close the socket that we are responsible for. + ACE_OS::closesocket (this->handle_); +} + +int +Sender::handle_input (ACE_HANDLE) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Reference count in Sender::handle_input() is %d\n"), + this->reference_count_.value ())); + + // + // In this test, this method is only called when the connection has + // been closed. Remove self from Reactor. + // + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Event loop thread calling Sender::close() ") + ACE_TEXT ("for handle %d\n"), + this->handle_)); + + this->close (); + + return 0; +} + +void +Sender::close (void) +{ + // Remove socket from Reactor (may fail if another thread has already + // removed the handle from the Reactor). + if (this->reactor() != 0) + this->reactor ()->remove_handler (this->handle_, + ACE_Event_Handler::ALL_EVENTS_MASK); + + // Remove self from connection cache (may fail if another thread has + // already removed "this" from the cache). + this->connection_cache_.remove_connection (this); +} + +ssize_t +Sender::send_message (void) +{ + ACE_Time_Value timeout (0, close_timeout * 1000); + + return ACE::send_n (this->handle_, + message, + message_size, + &timeout); +} + +class Event_Loop_Thread : public ACE_Task_Base +{ +public: + + Event_Loop_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor); + + int svc (void); + + ACE_Reactor &reactor_; + +}; + +class Receiver : public ACE_Task_Base +{ +public: + + Receiver (ACE_Thread_Manager &thread_manager, + ACE_HANDLE handle, + int nested_upcalls); + + ~Receiver (void); + + int svc (void); + + //FUZZ: disable check_for_lack_ACE_OS + int close (u_long flags); + //FUZZ: enable check_for_lack_ACE_OS + + int handle_input (ACE_HANDLE); + + int resume_handler (void); + + ACE_HANDLE handle_; + + int counter_; + + int nested_upcalls_; + + int nested_upcalls_level_; + +}; + +Receiver::Receiver (ACE_Thread_Manager &thread_manager, + ACE_HANDLE handle, + int nested_upcalls) + : ACE_Task_Base (&thread_manager), + handle_ (handle), + counter_ (1), + nested_upcalls_ (nested_upcalls), + nested_upcalls_level_ (0) +{ + // Enable reference counting. + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Reference count in Receiver::Receiver() is %d\n"), + this->reference_count_.value ())); +} + +Receiver::~Receiver (void) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Reference count in ~Receiver::Receiver() is %d\n"), + this->reference_count_.value ())); + + // Close the socket that we are responsible for. + ACE_OS::closesocket (this->handle_); +} + +int +Receiver::svc (void) +{ + // + // Continuously receive messages from the Sender. On error, exit + // thread. + // + + int result = 0; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Receiver::svc commencing, handle = %d\n"), + this->handle_)); + + while (result != -1) + { + result = + this->handle_input (this->handle_); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Receiver::svc terminating, handle = %d\n"), + this->handle_)); + return 0; +} + +int +Receiver::handle_input (ACE_HANDLE handle) +{ + char buf[message_size + 1]; + + ACE_Time_Value timeout (0, close_timeout * 1000); + + // Receive message. + ssize_t result = + ACE::recv_n (handle, + buf, + message_size, + &timeout); + + if (debug && result < 1) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Receiver::handle input, ") + ACE_TEXT("h = %d, result = %d %p\n"), + handle_, result, ACE_TEXT("ACE::recv_n"))); + + if (this->reactor ()) + this->reactor ()->resume_handler (handle); + + if (result == message_size) + { + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Message %d received on handle %d\n"), + this->counter_++, + handle)); + + if (this->thr_count () == 0 && + this->nested_upcalls_) + { + this->nested_upcalls_level_++; + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Nesting level %d\n"), + this->nested_upcalls_level_)); + + if ((this->nested_upcalls_level_ != max_nested_upcall_level) && + (global_event_loop_thread_variable != 0)) + global_event_loop_thread_variable->svc (); + + this->nested_upcalls_level_--; + return 0; + } + else + return 0; + } + else + { + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) /*** Problem in receiving message %d on handle") + ACE_TEXT (" %d: shutting down receiving thread ***/\n"), + this->counter_, + handle)); + + return -1; + } +} + +int +Receiver::resume_handler (void) +{ + /// The application takes responsibility of resuming the handler. + return ACE_APPLICATION_RESUMES_HANDLER; +} + +int +Receiver::close (u_long) +{ + // If threaded, we are responsible for deleting this instance when + // the thread completes. If not threaded, Reactor reference + // counting will handle the deletion of this instance. + delete this; + return 0; +} + +class Connector +{ +public: + + Connector (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + int nested_upcalls); + + //FUZZ: disable check_for_lack_ACE_OS + int connect (ACE_HANDLE &client_handle, + ACE_HANDLE &server_handle, + int run_receiver_thread); + //FUZZ: enable check_for_lack_ACE_OS + + ACE_Thread_Manager &thread_manager_; + + ACE_Reactor &reactor_; + + int nested_upcalls_; + +}; + +Connector::Connector (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + int nested_upcalls) + : thread_manager_ (thread_manager), + reactor_ (reactor), + nested_upcalls_ (nested_upcalls) +{ +} + +int +Connector::connect (ACE_HANDLE &client_handle, + ACE_HANDLE &server_handle, + int run_receiver_thread) +{ + // + // Create a connection and a receiver to receive messages on the + // connection. + // + + Pipe pipe; + int result = 0; + + for (int i = 0; i < pipe_open_attempts; ++i) + { + result = + pipe.open (); + + if (result == 0) + break; + + if (result == -1) + ACE_OS::sleep (pipe_retry_timeout); + } + + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + Receiver *receiver = + new Receiver (this->thread_manager_, + pipe.write_handle (), + this->nested_upcalls_); + + // Either the receiver is threaded or register it with the Reactor. + if (run_receiver_thread) + result = + receiver->activate (); + else + { + result = + this->reactor_.register_handler (pipe.write_handle (), + receiver, + ACE_Event_Handler::READ_MASK); + + // The reference count on the receiver was increased by the + // Reactor. + ACE_Event_Handler_var safe_receiver (receiver); + } + + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + client_handle = + pipe.read_handle (); + + server_handle = + pipe.write_handle (); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) New connection: client handle = %d, ") + ACE_TEXT ("server handle = %d\n"), + client_handle, server_handle)); + + return 0; +} + +Connection_Cache::Connection_Cache (void) +{ + // Initialize the connection cache. + this->entries_ = + new Entry[number_of_connections]; + + for (int i = 0; i < number_of_connections; ++i) + { + this->entries_[i].sender_ = 0; + this->entries_[i].state_ = NOT_IN_CACHE; + } +} + +int +Connection_Cache::find (Sender *sender) +{ + for (int i = 0; i < number_of_connections; ++i) + { + if (this->entries_[i].sender_ == sender) + return i; + } + + return -1; +} + +void +Connection_Cache::add_connection (Sender *sender) +{ + ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->lock_); + + // Make sure that the state of the connection cache is as + // expected. <sender> should not be already in the cache. + ACE_ASSERT (this->find (sender) == -1); + + int empty_index = + this->find (0); + + sender->add_reference (); + this->entries_[empty_index].sender_ = sender; + this->entries_[empty_index].state_ = BUSY; +} + +void +Connection_Cache::remove_connection (Sender *sender) +{ + ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->lock_); + + // Make sure that the state of the connection cache is as expected. + // remove_connection() may already have been called. + int index = + this->find (sender); + + if (index == -1) + return; + + // If we still have the sender, remove it. + sender->remove_reference (); + this->entries_[index].sender_ = 0; + this->entries_[index].state_ = NOT_IN_CACHE; +} + +Sender * +Connection_Cache::acquire_connection (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->lock_, 0); + + // Find a valid and IDLE sender. + + int index = -1; + + for (int i = 0; i < number_of_connections; ++i) + { + if (this->entries_[i].sender_ && + this->entries_[i].state_ == IDLE) + index = i; + } + + if (index == -1) + return 0; + + this->entries_[index].sender_->add_reference (); + this->entries_[index].state_ = BUSY; + + return this->entries_[index].sender_; +} + +void +Connection_Cache::release_connection (Sender *sender) +{ + ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->lock_); + + // Make sure that the state of the connection cache is as expected. + // remove_connection() may have already removed the connection from + // the cache. + int index = + this->find (sender); + + if (index == -1) + return; + + // If we still have the sender, idle it. + this->entries_[index].state_ = IDLE; +} + +ACE_SYNCH_MUTEX & +Connection_Cache::lock (void) +{ + return this->lock_; +} + +Connection_Cache::~Connection_Cache (void) +{ + for (int i = 0; i < number_of_connections; ++i) + { + if (this->entries_[i].sender_) + this->remove_connection (this->entries_[i].sender_); + } + + delete[] this->entries_; +} + +class Invocation_Thread : public ACE_Task_Base +{ +public: + + Invocation_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + Connection_Cache &connection_cache, + ACE_Auto_Event &new_connection_event, + int make_invocations, + int run_receiver_thread, + int nested_upcalls); + + int svc (void); + + Sender *create_connection (void); + + Connection_Cache &connection_cache_; + + ACE_Reactor &reactor_; + + ACE_Thread_Manager &thread_manager_; + + ACE_Auto_Event &new_connection_event_; + + int make_invocations_; + + int run_receiver_thread_; + + int nested_upcalls_; + +}; + +Invocation_Thread::Invocation_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + Connection_Cache &connection_cache, + ACE_Auto_Event &new_connection_event, + int make_invocations, + int run_receiver_thread, + int nested_upcalls) + : ACE_Task_Base (&thread_manager), + connection_cache_ (connection_cache), + reactor_ (reactor), + thread_manager_ (thread_manager), + new_connection_event_ (new_connection_event), + make_invocations_ (make_invocations), + run_receiver_thread_ (run_receiver_thread), + nested_upcalls_ (nested_upcalls) +{ +} + +Sender * +Invocation_Thread::create_connection (void) +{ + int result = 0; + + // Connector for creating new connections. + Connector connector (this->thread_manager_, + this->reactor_, + this->nested_upcalls_); + + // <server_handle> is a global variable. It will be used later by + // the Close_Socket_Thread. + result = + connector.connect (client_handle, + server_handle, + this->run_receiver_thread_); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + // Create a new sender. + Sender *sender = + new Sender (client_handle, + this->connection_cache_); + + // Register it with the cache. + this->connection_cache_.add_connection (sender); + + // + // There might be a race condition here. The sender has been added + // to the cache and is potentially available to other threads + // accessing the cache. Therefore, the other thread may use this + // sender and potentially close the sender before it even gets + // registered with the Reactor. + // + // This is resolved by marking the connection as busy when it is + // first added to the cache. And only once the thread creating the + // connection is done with it, it is marked a available in the + // cache. + // + // This order of registration is important. + // + + // Register the handle with the Reactor. + result = + this->reactor_.register_handler (client_handle, + sender, + ACE_Event_Handler::READ_MASK); +#if 0 + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); +#else + if (result != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) create_connection h %d, %p\n"), + client_handle, + ACE_TEXT ("register_handler"))); +#endif + return sender; +} + +int +Invocation_Thread::svc (void) +{ + int connection_counter = 0; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Invocation_Thread::svc commencing\n"))); + + for (int message_counter = 1;; ++message_counter) + { + // Get a connection from the cache. + Sender *sender = + this->connection_cache_.acquire_connection (); + + // If no connection is available in the cache, create a new one. + if (sender == 0) + { + if (connection_counter < number_of_connections) + { + sender = this->create_connection (); + + // This lets the Close_Socket_Thread know that the new + // connection has been created. + int result = + this->new_connection_event_.signal (); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + ++connection_counter; + message_counter = 1; + } + else + // Stop the thread, if the maximum number of connections + // for the test has been reached. + break; + } + + // The reference count on the sender was increased by the cache + // before it was returned to us. + ACE_Event_Handler_var safe_sender (sender); + + // If the test does not require making invocations, immediately + // release the connection. + if (!this->make_invocations_) + { + this->connection_cache_.release_connection (sender); + + // Sleep for a short while + ACE_OS::sleep (ACE_Time_Value (0, 10 * 1000)); + } + else + { + // Make invocation. + ssize_t result = + sender->send_message (); + + // If successful, release connection. + if (result == message_size) + { + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Message %d:%d delivered on handle %d\n"), + connection_counter, + message_counter, + sender->handle_)); + + this->connection_cache_.release_connection (sender); + } + else + { + // If failure in making invocation, close the sender. + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) /*** Problem in delivering message ") + ACE_TEXT ("%d:%d on handle %d: shutting down ") + ACE_TEXT ("invocation thread ***/\n"), + connection_counter, + message_counter, + sender->handle_)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Invocation thread calling ") + ACE_TEXT ("Sender::close() for handle %d\n"), + sender->handle_)); + + sender->close (); + } + } + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Invocation_Thread::svc calling end_reactor_event_loop\n"))); + + // Close the Reactor event loop. + this->reactor_.end_reactor_event_loop (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Invocation_Thread::svc terminating\n"))); + + return 0; +} + +class Close_Socket_Thread : public ACE_Task_Base +{ +public: + + Close_Socket_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + ACE_Auto_Event &new_connection_event, + int make_invocations, + int run_receiver_thread); + + int svc (void); + + ACE_Auto_Event &new_connection_event_; + + ACE_Reactor &reactor_; + + int make_invocations_; + + int run_receiver_thread_; + +}; + +Close_Socket_Thread::Close_Socket_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + ACE_Auto_Event &new_connection_event, + int make_invocations, + int run_receiver_thread) + : ACE_Task_Base (&thread_manager), + new_connection_event_ (new_connection_event), + reactor_ (reactor), + make_invocations_ (make_invocations), + run_receiver_thread_ (run_receiver_thread) +{ +} + +int +Close_Socket_Thread::svc (void) +{ + ACE_OS::srand ((u_int) ACE_OS::time ()); + ACE_Time_Value timeout (0, close_timeout * 1000); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Close_Socket_Thread::svc commencing\n"))); + + for (; !this->reactor_.reactor_event_loop_done ();) + { + // Wait for the new connection to be established. + int result = + this->new_connection_event_.wait (&timeout, + 0); + ACE_ASSERT (result == 0 || + (result == -1 && errno == ETIME)); + + if (result == -1 && + errno == ETIME) + continue; + + // Sleep for half a second. + ACE_OS::sleep (timeout); + + int close_client = 0; + + // If the invocation thread is making invocations and if the + // receiver is threaded, either socket can be closed. + if (this->make_invocations_ && + this->run_receiver_thread_) + // Randomize which socket to close. + close_client = ACE_OS::rand () % 2; + + // If the invocation thread is making invocations, only close + // the client socket. + else if (this->make_invocations_) + close_client = 1; + else + // If the receiver is threaded, only close the server socket. + close_client = 0; + + if (close_client) + { + // Close the client socket. + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Close socket thread closing client ") + ACE_TEXT ("handle %d\n"), + client_handle)); + + ACE_OS::shutdown (client_handle, ACE_SHUTDOWN_BOTH); + ACE_OS::closesocket (client_handle); + } + else + { + // Close the server socket. + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Close socket thread closing server ") + ACE_TEXT ("handle %d\n"), + server_handle)); + ACE_OS::shutdown (server_handle, ACE_SHUTDOWN_BOTH); + ACE_OS::closesocket (server_handle); + } + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Close_Socket_Thread::svc terminating\n"))); + + return 0; +} + +Event_Loop_Thread::Event_Loop_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor) + : ACE_Task_Base (&thread_manager), + reactor_ (reactor) +{ +} + +int +Event_Loop_Thread::svc (void) +{ + // Simply run the event loop. + this->reactor_.owner (ACE_Thread::self ()); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Event_Loop_Thread::svc commencing\n"))); + + while (!this->reactor_.reactor_event_loop_done ()) + { + this->reactor_.handle_events (); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Event_Loop_Thread::svc terminating\n"))); + + return 0; +} + +class Purger_Thread : public ACE_Task_Base +{ +public: + + Purger_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + Connection_Cache &connection_cache); + + int svc (void); + + ACE_Reactor &reactor_; + + Connection_Cache &connection_cache_; + +}; + +Purger_Thread::Purger_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + Connection_Cache &connection_cache) + : ACE_Task_Base (&thread_manager), + reactor_ (reactor), + connection_cache_ (connection_cache) +{ +} + +int +Purger_Thread::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Purger_Thread::svc commencing\n"))); + + for (; !this->reactor_.reactor_event_loop_done ();) + { + // Get a connection from the cache. + Sender *sender = + this->connection_cache_.acquire_connection (); + + // If no connection is available in the cache, sleep for a while. + if (sender == 0) + ACE_OS::sleep (ACE_Time_Value (0, 10 * 1000)); + else + { + // The reference count on the sender was increased by the + // cache before it was returned to us. + ACE_Event_Handler_var safe_sender (sender); + + // Actively close the connection. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Purger thread calling Sender::close() ") + ACE_TEXT ("for handle %d\n"), + sender->handle_)); + + sender->close (); + } + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Purger_Thread::svc terminating\n"))); + + return 0; +} + +void +testing (ACE_Reactor *reactor, + int make_invocations, + int run_event_loop_thread, + int run_purger_thread, + int run_receiver_thread, + int nested_upcalls) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n(%t) Configuration: \n") + ACE_TEXT ("\tInvocation thread = %d\n") + ACE_TEXT ("\tEvent Loop thread = %d\n") + ACE_TEXT ("\tPurger thread = %d\n") + ACE_TEXT ("\tReceiver thread = %d\n") + ACE_TEXT ("\tNested Upcalls = %d\n\n"), + make_invocations, + run_event_loop_thread, + run_purger_thread, + run_receiver_thread, + nested_upcalls)); + + ACE_Thread_Manager thread_manager; + + int result = 0; + + // Create the connection cache. + Connection_Cache connection_cache; + ACE_Auto_Event new_connection_event; + + // Create the invocation thread. + Invocation_Thread invocation_thread (thread_manager, + *reactor, + connection_cache, + new_connection_event, + make_invocations, + run_receiver_thread, + nested_upcalls); + + result = + invocation_thread.activate (); + ACE_ASSERT (result == 0); + + // Create the thread for closing the server socket. + Close_Socket_Thread close_socket_thread (thread_manager, + *reactor, + new_connection_event, + make_invocations, + run_receiver_thread); + result = + close_socket_thread.activate (); + ACE_ASSERT (result == 0); + + global_event_loop_thread_variable = 0; + + // Create a thread to run the event loop. + Event_Loop_Thread event_loop_thread (thread_manager, + *reactor); + if (run_event_loop_thread) + { + global_event_loop_thread_variable = + &event_loop_thread; + + result = + event_loop_thread.activate (); + ACE_ASSERT (result == 0); + } + + // Create a thread to run the purger. + Purger_Thread purger_thread (thread_manager, + *reactor, + connection_cache); + if (run_purger_thread) + { + result = + purger_thread.activate (); + ACE_ASSERT (result == 0); + } + + // Wait for threads to exit. + result = thread_manager.wait (); + ACE_ASSERT (result == 0); + + // Set the global variable to zero again because the + // event_loop_thread exists on the stack and now + // gets destructed. + global_event_loop_thread_variable = 0; +} + +template <class REACTOR_IMPL> +class test +{ +public: + test (int ignore_nested_upcalls, + int require_event_loop_thread); +}; + +template <class REACTOR_IMPL> +test<REACTOR_IMPL>::test (int ignore_nested_upcalls, + int require_event_loop_thread) +{ + for (int i = 0; + i < (int) (sizeof test_configs / (sizeof (int) * number_of_options)); + i++) + { + if ((make_invocations == -1 || + make_invocations == test_configs[i][0]) && + (run_event_loop_thread == -1 || + run_event_loop_thread == test_configs[i][1]) && + (run_purger_thread == -1 || + run_purger_thread == test_configs[i][2]) && + (run_receiver_thread == -1 || + run_receiver_thread == test_configs[i][3]) && + (nested_upcalls == -1 || + nested_upcalls == test_configs[i][4])) + { + +#if 0 // defined (linux) + + // @@ I am not sure why but when <make_invocations> is 0 and + // there is no purger thread, the receiver thread does not + // notice that the connection has been closed. + if (!test_configs[i][0] && !test_configs[i][2]) + continue; + + // @@ Linux also does not work correctly in the following + // case: Invocation thread starts and sends messages filling + // the socket buffer. It then blocks in write(). In the + // meantime, the close connection thread closes the socket + // used by invocation thread. However, the invocation thread + // does not notice this as it does not return from write(). + // Meanwhile, the event loop thread notices that a socket in + // it's wait set has been closed, and starts to spin in + // handle_events() since the invocation thread is not taking + // out the closed handle from the Reactor's wait set. + if (test_configs[i][0] && test_configs[i][1] && !test_configs[i][3]) + continue; + +#endif /* linux */ + + if (test_configs[i][4] && ignore_nested_upcalls) + continue; + + if (!test_configs[i][1] && require_event_loop_thread) + continue; + + ACE_Reactor reactor (new REACTOR_IMPL, + 1); + + testing (&reactor, + test_configs[i][0], + test_configs[i][1], + test_configs[i][2], + test_configs[i][3], + test_configs[i][4]); + } + } +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:b:c:d:f:g:k:l:m:n:o:uz:")); + + int cc; + while ((cc = get_opt ()) != -1) + { + switch (cc) + { + case 'a': + test_select_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + test_tp_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + test_wfmo_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + test_dev_poll_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'f': + number_of_connections = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'g': + close_timeout = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'k': + make_invocations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'l': + run_event_loop_thread = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'm': + run_purger_thread = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + run_receiver_thread = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'o': + nested_upcalls = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'z': + debug = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'u': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\nusage: %s \n\n") + ACE_TEXT ("\t[-a test Select Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-b test TP Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-c test WFMO Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-d test Dev Poll Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-f number of connections] (defaults to %d)\n") + ACE_TEXT ("\t[-g close timeout] (defaults to %d)\n") + ACE_TEXT ("\t[-k make invocations] (defaults to %d)\n") + ACE_TEXT ("\t[-l run event loop thread] (defaults to %d)\n") + ACE_TEXT ("\t[-m run purger thread] (defaults to %d)\n") + ACE_TEXT ("\t[-n run receiver thread] (defaults to %d)\n") + ACE_TEXT ("\t[-o nested upcalls] (defaults to %d)\n") + ACE_TEXT ("\t[-z debug] (defaults to %d)\n") + ACE_TEXT ("\n"), + argv[0], + test_select_reactor, + test_tp_reactor, + test_wfmo_reactor, + test_dev_poll_reactor, + number_of_connections, + close_timeout, + make_invocations, + run_event_loop_thread, + run_purger_thread, + run_receiver_thread, + nested_upcalls, + debug)); + return -1; + } + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_Reference_Counted_Event_Handler_Test")); + + // Validate options. + int result = + parse_args (argc, argv); + if (result != 0) + return result; + +#if defined (SIGPIPE) && !defined (ACE_LACKS_UNIX_SIGNALS) + // There's really no way to deal with this in a portable manner, so + // we just have to suck it up and get preprocessor conditional and + // ugly. + // + // Impractical to have each call to the ORB protect against the + // implementation artifact of potential writes to dead connections, + // as it'd be way expensive. Do it here; who cares about SIGPIPE in + // these kinds of applications, anyway? + (void) ACE_OS::signal (SIGPIPE, (ACE_SignalHandler) SIG_IGN); +#endif /* SIGPIPE */ + + int ignore_nested_upcalls = 1; + int perform_nested_upcalls = 0; + + int event_loop_thread_required = 1; + int event_loop_thread_not_required = 0; + + if (test_select_reactor) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\n(%t) Testing Select Reactor....\n\n"))); + + test<ACE_Select_Reactor> test (ignore_nested_upcalls, + event_loop_thread_not_required); + ACE_UNUSED_ARG (test); + } + + if (test_tp_reactor) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\n(%t) Testing TP Reactor....\n\n"))); + + test<ACE_TP_Reactor> test (perform_nested_upcalls, + event_loop_thread_not_required); + ACE_UNUSED_ARG (test); + } + +#if defined (ACE_HAS_EVENT_POLL) + + if (test_dev_poll_reactor) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\n(%t) Testing Dev Poll Reactor....\n\n"))); + + test<ACE_Dev_Poll_Reactor> test (perform_nested_upcalls, + event_loop_thread_not_required); + ACE_UNUSED_ARG (test); + } + +#endif + +#if defined (ACE_WIN32) + + if (test_wfmo_reactor) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\n(%t) Testing WFMO Reactor....\n\n"))); + + test<ACE_WFMO_Reactor> test (ignore_nested_upcalls, + event_loop_thread_required); + ACE_UNUSED_ARG (test); + } + +#else /* ACE_WIN32 */ + + ACE_UNUSED_ARG (event_loop_thread_required); + +#endif /* ACE_WIN32 */ + + ACE_END_TEST; + + return 0; +} + +#else /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_Reference_Counted_Event_Handler_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/MT_Reference_Counted_Notify_Test.cpp b/ACE/tests/MT_Reference_Counted_Notify_Test.cpp new file mode 100644 index 00000000000..713e81d6a5e --- /dev/null +++ b/ACE/tests/MT_Reference_Counted_Notify_Test.cpp @@ -0,0 +1,463 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MT_Reference_Counted_Notify_Test.cpp +// +// = DESCRIPTION +// This test is used to check reference counting of the event +// handlers when it interacts with the reactor notification +// mechanism. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Dev_Poll_Reactor.h" +#include "ace/Task.h" +#include "ace/Get_Opt.h" + +ACE_RCSID(tests, MT_Reference_Counted_Notify_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +static int test_select_reactor = 1; +static int test_tp_reactor = 1; +static int test_wfmo_reactor = 1; +static int test_dev_poll_reactor = 1; +static int test_empty_notify = 1; +static int test_simple_notify = 1; +static int test_reference_counted_notify = 1; +static int iterations = 5; +static int debug = 1; + +class Reference_Counted_Event_Handler : public ACE_Event_Handler +{ +public: + + Reference_Counted_Event_Handler (void); + + ~Reference_Counted_Event_Handler (void); + + int handle_input (ACE_HANDLE); + + ACE_Event_Handler::Reference_Count add_reference (void); + + ACE_Event_Handler::Reference_Count remove_reference (void); + +}; + +Reference_Counted_Event_Handler::Reference_Counted_Event_Handler (void) +{ + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + if (debug) + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("Reference count in Reference_Counted_Event_Handler() ") + ACE_TEXT ("is %d\n"), + this->reference_count_.value ())); +} + +Reference_Counted_Event_Handler::~Reference_Counted_Event_Handler (void) +{ + if (debug) + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("Reference count in ~Reference_Counted_Event_Handler() ") + ACE_TEXT ("is %d\n"), + this->reference_count_.value ())); + + if (0 != this->reference_count_.value ()) + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("Reference count in ~Reference_Counted_Event_Handler() ") + ACE_TEXT ("should be 0 but is %d\n"), + this->reference_count_.value ())); +} + +int +Reference_Counted_Event_Handler::handle_input (ACE_HANDLE) +{ + if (debug) + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("Reference count in Reference_Counted_Event_Handler::") + ACE_TEXT ("handle_input() is %d\n"), + this->reference_count_.value ())); + + if (2 != this->reference_count_.value ()) + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("Reference count in Reference_Counted_Event_Handler::") + ACE_TEXT ("handle_input() should be 2 but is %d\n"), + this->reference_count_.value ())); + + return 0; +} + +ACE_Event_Handler::Reference_Count +Reference_Counted_Event_Handler::add_reference (void) +{ + ACE_Event_Handler::Reference_Count reference_count = + this->ACE_Event_Handler::add_reference (); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Reference count after add_reference() is %d\n"), + this->reference_count_.value ())); + + return reference_count; +} + +ACE_Event_Handler::Reference_Count +Reference_Counted_Event_Handler::remove_reference (void) +{ + ACE_Event_Handler::Reference_Count reference_count = + this->ACE_Event_Handler::remove_reference (); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Reference count after remove_reference() is %d\n"), + reference_count)); + + return reference_count; +} + +class Simple_Event_Handler : public ACE_Event_Handler +{ +public: + + Simple_Event_Handler (int notifies); + + ~Simple_Event_Handler (void); + + int handle_input (ACE_HANDLE); + + int notifies_; +}; + +Simple_Event_Handler::Simple_Event_Handler (int notifies) + : notifies_ (notifies) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Simple_Event_Handler()\n"))); +} + +Simple_Event_Handler::~Simple_Event_Handler (void) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("~Simple_Event_Handler()\n"))); +} + +int +Simple_Event_Handler::handle_input (ACE_HANDLE) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Simple_Event_Handler::handle_input()\n"))); + + this->notifies_--; + + if (this->notifies_ == 0) + delete this; + + return 0; +} + +class Event_Loop_Thread : public ACE_Task_Base +{ +public: + + Event_Loop_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + int extra_iterations_needed); + + int svc (void); + + ACE_Reactor &reactor_; + + int extra_iterations_needed_; +}; + +Event_Loop_Thread::Event_Loop_Thread (ACE_Thread_Manager &thread_manager, + ACE_Reactor &reactor, + int extra_iterations_needed) + : ACE_Task_Base (&thread_manager), + reactor_ (reactor), + extra_iterations_needed_ (extra_iterations_needed) +{ +} + +int +Event_Loop_Thread::svc (void) +{ + int counter = 0; + + // Simply run the event loop. + this->reactor_.owner (ACE_Thread::self ()); + + while (1) + { + counter++; + + if (debug) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event Loop iteration %d....\n"), + counter)); + + this->reactor_.handle_events (); + + if (counter == + iterations + this->extra_iterations_needed_) + break; + } + + return 0; +} + +void +notify (ACE_Reactor &reactor, + ACE_Event_Handler *event_handler, + int extra_iterations_needed) +{ + ACE_Thread_Manager thread_manager; + + // Create a thread to run the event loop. + Event_Loop_Thread event_loop_thread (thread_manager, + reactor, + extra_iterations_needed); + + int result = + event_loop_thread.activate (); + ACE_ASSERT (result == 0); + + for (int i = 0; + i < iterations; + ++i) + { + ACE_OS::sleep (ACE_Time_Value (0, 500 * 1000)); + + result = reactor.notify (event_handler, + ACE_Event_Handler::READ_MASK); + + ACE_ASSERT (result == 0); + } + + thread_manager.wait (); +} + +template <class REACTOR_IMPLEMENTATION> +class test +{ +public: + test (int extra_iterations_needed); +}; + +template <class REACTOR_IMPLEMENTATION> +test<REACTOR_IMPLEMENTATION>::test (int extra_iterations_needed) +{ + if (test_empty_notify) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n\nTesting empty notifies...\n\n"))); + + REACTOR_IMPLEMENTATION impl; + ACE_Reactor reactor (&impl, 0); + + notify (reactor, + 0, + extra_iterations_needed); + } + + + if (test_simple_notify) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n\nTesting simple notifies...\n\n"))); + + REACTOR_IMPLEMENTATION impl; + ACE_Reactor reactor (&impl, 0); + + Simple_Event_Handler *simple_event_handler = + new Simple_Event_Handler (iterations); + + notify (reactor, + simple_event_handler, + extra_iterations_needed); + } + + if (test_reference_counted_notify) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\nTesting reference counted notifies...\n\n"))); + + REACTOR_IMPLEMENTATION impl; + ACE_Reactor reactor (&impl, 0); + + Reference_Counted_Event_Handler *reference_counted_event_handler = + new Reference_Counted_Event_Handler; + + ACE_Event_Handler_var safe_event_handler (reference_counted_event_handler); + + notify (reactor, + reference_counted_event_handler, + extra_iterations_needed); + } +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:b:c:d:e:f:g:z:")); + + int cc; + while ((cc = get_opt ()) != -1) + { + switch (cc) + { + case 'a': + test_select_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + test_tp_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + test_wfmo_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + test_dev_poll_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'e': + test_empty_notify = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'f': + test_simple_notify = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'g': + test_reference_counted_notify = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'z': + debug = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\nusage: %s \n\n") + ACE_TEXT ("\t[-a test Select Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-b test TP Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-c test WFMO Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-d test Dev Poll Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-e test empty notify] (defaults to %d)\n") + ACE_TEXT ("\t[-f test simple notify] (defaults to %d)\n") + ACE_TEXT ("\t[-g test reference counted notify] (defaults to %d)\n") + ACE_TEXT ("\t[-z debug] (defaults to %d)\n") + ACE_TEXT ("\n"), + argv[0], + test_select_reactor, + test_tp_reactor, + test_wfmo_reactor, + test_dev_poll_reactor, + test_empty_notify, + test_simple_notify, + test_reference_counted_notify, + debug)); + return -1; + } + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_Reference_Counted_Notify_Test")); + + // Validate options. + int result = + parse_args (argc, argv); + if (result != 0) + return result; + + int extra_iterations_needed = 1; + int extra_iterations_not_needed = 0; + + if (test_select_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting Select Reactor....\n\n")); + + test<ACE_Select_Reactor> test (extra_iterations_not_needed); + ACE_UNUSED_ARG (test); + } + + if (test_tp_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting TP Reactor....\n\n")); + + test<ACE_TP_Reactor> test (extra_iterations_not_needed); + ACE_UNUSED_ARG (test); + } + +#if defined (ACE_HAS_EVENT_POLL) + + if (test_dev_poll_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting Dev Poll Reactor....\n\n")); + + test<ACE_Dev_Poll_Reactor> test (extra_iterations_not_needed); + ACE_UNUSED_ARG (test); + } + +#endif + +#if defined (ACE_WIN32) + + if (test_wfmo_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting WFMO Reactor....\n\n")); + + test<ACE_WFMO_Reactor> test (extra_iterations_needed); + ACE_UNUSED_ARG (test); + } + +#else /* ACE_WIN32 */ + + ACE_UNUSED_ARG (extra_iterations_needed); + +#endif /* ACE_WIN32 */ + + ACE_END_TEST; + + return 0; +} + +#else /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_Reference_Counted_Notify_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/MT_SOCK_Test.cpp b/ACE/tests/MT_SOCK_Test.cpp new file mode 100644 index 00000000000..adfd3dc58ee --- /dev/null +++ b/ACE/tests/MT_SOCK_Test.cpp @@ -0,0 +1,442 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// MT_SOCK_Test.cpp +// +// = DESCRIPTION +// This is a multi-threaded torture test of the +// <ACE_SOCK_Acceptor> and <ACE_SOCK_Connector> classes. The test +// forks 30 processes or spawns 30 threads (depending upon the +// platform) and then executes client and server allowing them to +// connect and exchange data. Note that most of the connections +// will fail since we're overrunning the size of the listen queue +// for the acceptor-mode socket. +// +// = AUTHOR +// Doug Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Handle_Set.h" +#include "ace/Time_Value.h" + +ACE_RCSID(tests, MT_SOCK_Test, "$Id$") + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// Normally the test will have BACKLOG < NUM_CLIENTS to force some +// of the connections to fail. +// Do NOT use ACE_DEFAULT_BACKLOG here, because that will likely +// be set to some other value. (i.e. Win32 = SOMAXCONN) +static const int BACKLOG = 5; +static const int NUM_CLIENTS = 30; + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg; + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_DEFAULT_SERVER_HOST); + ACE_INET_Addr client_addr; + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; +#if defined (ACE_HAS_BROKEN_NON_BLOCKING_CONNECTS) + ACE_Time_Value *timeout = 0; +#else + ACE_Time_Value tv (ACE_DEFAULT_TIMEOUT); + ACE_Time_Value *timeout = &tv; +#endif /* ACE_HAS_BROKEN_NON_BLOCKING_CONNECTS */ + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) client: Connecting...\n"))); + // Initiate timed connection with server. + + // Attempt a timed connect to the server. + if (con.connect (cli_stream, + server_addr, + timeout) == -1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("client: Connection timed out."))); + return 0; + } + + if (cli_stream.get_local_addr (client_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("client: get_local_addr")), + 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client: Connected at %d\n"), + client_addr.get_port_number ())); + + if (cli_stream.disable (ACE_NONBLOCK) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("client: disable"))); + + // Send data to server (correctly handles "incomplete writes"). + + ACE_DEBUG ((LM_DEBUG, "(%P|%t) client: Sending data...\n")); + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + if (cli_stream.send_n (c, 1) == -1) + { + // This is, I believe, more of an issue with WinXP-64 _server_ + // side, but we can trap it here since we know we're connecting + // to localhost. Some Windows versions will appear to accept + // connections at the TCP level past the listen backlog but if + // data arrives before the actual application-level accept() occurs, + // the connection is reset. This is caused when we trip the Windows + // SYN attack prevention (http://technet2.microsoft.com/WindowsServer/ + // en/library/910c8482-e5e5-4e2c-9ea4-11301ddfc4661033.mspx?mfr=true) + // So, if we get a reset on the first send, don't flag the error - + // just note it and act like the connection was refused. + if (c == ACE_ALPHABET && errno == ECONNRESET) // First byte sent + { + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client: Connection refused (delayed)\n"))); + cli_stream.close (); + return 0; + } + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) (errno %d) %p\n"), errno, + ACE_TEXT ("client: send_n"))); + ACE_ERROR ((LM_ERROR, "client: Closing stream.\n")); + cli_stream.close(); + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client: Closing writer...\n"))); + + // Explicitly close the writer-side of the connection. + if (cli_stream.close_writer () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("client: close_writer"))); + char buf[1]; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client: Waiting for server handshake...\n"))); + + // Wait for handshake with server. + if (cli_stream.recv_n (buf, 1) != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("client: recv_n"))); + + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client: Handshake received. Closing stream.\n"))); + + // Close the connection completely. + if (cli_stream.close () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("client: close"))); + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Acceptor *peer_acceptor = + static_cast<ACE_SOCK_Acceptor *> (arg); + + if (peer_acceptor->enable (ACE_NONBLOCK) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: enable acceptor"))); + + // Keep these objects out here to prevent excessive constructor + // calls... + ACE_SOCK_Stream new_stream; + ACE_INET_Addr cli_addr; + ACE_Handle_Set handle_set; + const ACE_Time_Value def_timeout (ACE_DEFAULT_TIMEOUT); + ACE_Time_Value tv (def_timeout); + + // We want some of the clients to get connection failures, but on + // a really fast machine with a good network card and multiple + // processors this may never happen. + // Add a sleep() to allow the client threads to complete. + ACE_OS::sleep(def_timeout); + + int num_clients_connected = 0; + + // Performs the iterative server activities. + for (;;) + { + char buf[BUFSIZ]; + + handle_set.reset (); + handle_set.set_bit (peer_acceptor->get_handle ()); + + ACE_DEBUG((LM_DEBUG, "(%P|%t) server: Waiting for connection...\n")); + + int select_width; +# if defined (ACE_WIN64) + // This arg is ignored on Windows and causes pointer truncation + // warnings on 64-bit compiles. + select_width = 0; +# else + select_width = int (peer_acceptor->get_handle ()) + 1; +# endif /* ACE_WIN64 */ + int result = ACE_OS::select (select_width, handle_set, 0, 0, &tv); + ACE_ASSERT (tv == def_timeout); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: select acceptor")), + 0); + else if (result == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: Test finished.\n"))); + // The meaning of the backlog parameter for listen() varies by + // platform. For some reason lost to history, the specified value + // is typically backlog * 1.5, backlog * 1.5 + 1, or event taken + // literally as on Windows. We'll accept any number less than + // backlog * 2 as valid. + if (num_clients_connected >= BACKLOG * 2) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) server: Incorrect # client ") + ACE_TEXT ("connections. Expected:%d-%d Actual:%d\n"), + BACKLOG, BACKLOG * 2, num_clients_connected)); + return 0; + } + + // Create a new ACE_SOCK_Stream endpoint (note automatic restart + // if errno == EINTR). + + while ((result = peer_acceptor->accept (new_stream, + &cli_addr)) != -1) + { + const char *t = ACE_ALPHABET; + + ++num_clients_connected; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: Client %C connected from %d\n"), + cli_addr.get_host_name (), + cli_addr.get_port_number ())); + + // Enable non-blocking I/O. + if (new_stream.enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: enable non blocking i/o")), + 0); + handle_set.reset (); + handle_set.set_bit (new_stream.get_handle ()); + + // Read data from client (terminate on error). + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: Waiting for data...\n"))); + + for (ssize_t r_bytes; ;) + { + int select_width; +# if defined (ACE_WIN64) + // This arg is ignored on Windows and causes pointer truncation + // warnings on 64-bit compiles. + select_width = 0; +# else + select_width = int (new_stream.get_handle ()) + 1; +# endif /* ACE_WIN64 */ + if (ACE_OS::select (select_width, + handle_set, + 0, 0, 0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select")), + 0); + + ACE_DEBUG ((LM_DEBUG, "(%P|%t) server: Receiving data...\n")); + + while ((r_bytes = new_stream.recv (buf, 1)) > 0) + { + ACE_ASSERT (*t == buf[0]); + t++; + } + + ACE_DEBUG((LM_DEBUG, "(%P|%t) server: Received data.\n")); + + if (r_bytes == 0) + { + // Handshake back with client. + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: Connection closed by client.\n"))); + + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: Sending handshake.\n"))); + + if (new_stream.send_n ("", 1) != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: send_n"))); + + ACE_DEBUG ((LM_DEBUG, "(%P|%t) server: Closing stream.\n")); + + // Close endpoint. + if (new_stream.close () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: close"))); + break; + } + else if (r_bytes == -1) + { + if (errno == EWOULDBLOCK || errno == EAGAIN) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: (EWOULDBLOCK) Waiting for more data...\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: recv_n")), + 0); + } + } + } + if (result == -1) + { + if (errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) server: No more connections pending.\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server: accept"))); + } + } + ACE_NOTREACHED (return 0); +} + +#endif /* !ACE_LACKS_FORK || ACE_HAS_THREADS */ + +static void +spawn (int num_clients) +{ + // Acceptor + ACE_SOCK_Acceptor peer_acceptor; + + // Create a server address. + ACE_INET_Addr server_addr; + + // Bind listener to any port and then find out what the port was. + if (peer_acceptor.open (ACE_Addr::sap_any, 0, PF_UNSPEC, BACKLOG) == -1 + || peer_acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("spawn: open"))); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + for (int i = 0; i < num_clients; i++) + { + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), "spawn: fork failed")); + i = num_clients; + // Break out of 'for' loop. + break; + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + break; + } + } + + server ((void *) &peer_acceptor); + + peer_acceptor.close(); + + // Reap the child pids. + for (pid_t pid; (pid = ACE_OS::wait ()) != -1; ) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawn: reaping pid %d\n"), pid)); + +#elif defined (ACE_HAS_THREADS) + + ACE_DEBUG((LM_DEBUG, "Spawning server...\n")); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + (void *) &peer_acceptor, + THR_BOUND | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("spawn: failed"), + 1)); + + ACE_DEBUG((LM_DEBUG, "Spawning %d clients...\n", num_clients)); + + if (ACE_Thread_Manager::instance ()->spawn_n + (num_clients, + ACE_THR_FUNC (client), + (void *) &server_addr, + THR_BOUND | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("spawn: failed 2"), + 1)); + + ACE_DEBUG((LM_DEBUG, "Waiting for threads to finish...\n")); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("spawn: only one thread may be run") + ACE_TEXT (" in a process on this platform\n"))); +#endif /* !ACE_LACKS_FORK */ + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Threads complete. Closing Acceptor.\n"))); + + peer_acceptor.close (); + } +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("MT_SOCK_Test")); + + spawn (NUM_CLIENTS); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Main.cpp b/ACE/tests/Main.cpp new file mode 100644 index 00000000000..44b92128394 --- /dev/null +++ b/ACE/tests/Main.cpp @@ -0,0 +1,30 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Main.cpp +// +// = DESCRIPTION +// This is a wrapper for the test programs. It obviates the test cpp's +// from having to always include OS.h. +// +// = AUTHOR +// Don Hinton <dhinton@dresystems.com> +// +// ============================================================================ + +#include "ace/OS_main.h" +#if defined (ACE_HAS_WINCE) +# include "ace/ACE.h" +#endif /* ACE_HAS_WINCE */ +int run_main (int argc, ACE_TCHAR *argv[]); + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + return run_main (argc, argv); +} diff --git a/ACE/tests/Makefile.am b/ACE/tests/Makefile.am new file mode 100644 index 00000000000..0f9e331da91 --- /dev/null +++ b/ACE/tests/Makefile.am @@ -0,0 +1,3330 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## ./bin/mwc.pl -type automake -noreldefs ACE.mwc + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +SUBDIRS = \ + . \ + SSL + + +## Makefile.Test_Output.am + +noinst_SCRIPTS = run_test.pl run_test.lst +dist_check_SCRIPTS = run_test.pl run_test.lst +TESTS = $(noinst_PROGRAMS) +TESTS_ENVIRONMENT = $(srcdir)/run_test.pl -t + + +noinst_LTLIBRARIES = libTest_Output.la + +libTest_Output_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DTEST_OUTPUT_BUILD_DLL + +libTest_Output_la_SOURCES = \ + Test_Output.cpp + +noinst_HEADERS = \ + Test_Output_Export.h \ + test_config.h + +## Makefile.ACE_Init_Test.am + +noinst_PROGRAMS = ACE_Init_Test + +ACE_Init_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +ACE_Init_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + ACE_Init_Test.cpp \ + ACE_Init_Test.h + +ACE_Init_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +EXTRA_DIST = \ + ACE_Init_Test.rc + + +## Makefile.ACE_Test.am + +noinst_PROGRAMS += ACE_Test + +ACE_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +ACE_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + ACE_Test.cpp + +ACE_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.ARGV_Test.am + +noinst_PROGRAMS += ARGV_Test + +ARGV_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +ARGV_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + ARGV_Test.cpp + +ARGV_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Aio_Platform_Test.am + +noinst_PROGRAMS += Aio_Platform_Test + +Aio_Platform_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Aio_Platform_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Aio_Platform_Test.cpp + +Aio_Platform_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Arg_Shifter_Test.am + +noinst_PROGRAMS += Arg_Shifter_Test + +Arg_Shifter_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Arg_Shifter_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Arg_Shifter_Test.cpp + +Arg_Shifter_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Array_Map_Test.am + +noinst_PROGRAMS += Array_Map_Test + +Array_Map_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Array_Map_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Array_Map_Test.cpp + +Array_Map_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Atomic_Op_Test.am + +noinst_PROGRAMS += Atomic_Op_Test + +Atomic_Op_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Atomic_Op_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Atomic_Op_Test.cpp + +Atomic_Op_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Auto_Event_Test.am + +noinst_PROGRAMS += Auto_Event_Test + +Auto_Event_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Auto_Event_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Auto_Event_Test.cpp + +Auto_Event_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Auto_IncDec_Test.am + +noinst_PROGRAMS += Auto_IncDec_Test + +Auto_IncDec_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Auto_IncDec_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Auto_IncDec_Test.cpp + +Auto_IncDec_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Barrier_Test.am + +noinst_PROGRAMS += Barrier_Test + +Barrier_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Barrier_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Barrier_Test.cpp + +Barrier_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Based_Pointer_Test_Lib.am + +if !BUILD_ACE_FOR_TAO + +noinst_LTLIBRARIES += libBased_Pointer_Test_Lib.la + +libBased_Pointer_Test_Lib_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_SVC_BUILD_DLL + +libBased_Pointer_Test_Lib_la_SOURCES = \ + Based_Pointer_Test_Lib.cpp + +noinst_HEADERS += \ + ACE_Init_Test.h \ + ACE_Init_TestDlg.h \ + ACE_Init_Test_Resource.h \ + ACE_Init_Test_StdAfx.h \ + Bound_Ptr_Test.h \ + CE_fostream.h \ + Cache_Map_Manager_Test.h \ + Cached_Accept_Conn_Test.h \ + Cached_Conn_Test.h \ + Collection_Test.h \ + Config_Test.h \ + Conn_Test.h \ + DLL_Test.h \ + DLL_Test_Impl.h \ + DLL_Test_Parent.h \ + DLL_Test_Parent_Export.h \ + Framework_Component_DLL.h \ + Framework_Component_DLL_Export.h \ + Framework_Component_Test.h \ + MEM_Stream_Test.h \ + MT_Reactor_Timer_Test.h \ + Malloc_Test.h \ + Map_Test.h \ + Max_Default_Port_Test.h \ + Message_Queue_Test_Ex.h \ + Network_Adapters_Test.h \ + NonBlocking_Conn_Test.h \ + Priority_Reactor_Test.h \ + Proactor_Test.h \ + Process_Strategy_Test.h \ + QtReactor_Test.h \ + RB_Tree_Test.h \ + Reactor_Performance_Test.h \ + Refcounted_Auto_Ptr_Test.h \ + Service_Config_DLL.h \ + Service_Config_DLL_Export.h \ + TP_Reactor_Test.h \ + TSS_Test_Errno.h \ + Task_Ex_Test.h \ + Test_Output_Export.h \ + Thread_Pool_Reactor_Resume_Test.h \ + Thread_Pool_Reactor_Test.h \ + Upgradable_RW_Test.h \ + test_config.h + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Based_Pointer_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Based_Pointer_Test + +Based_Pointer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Based_Pointer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Based_Pointer_Test.cpp + +Based_Pointer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Basic_Types_Test.am + +noinst_PROGRAMS += Basic_Types_Test + +Basic_Types_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Basic_Types_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Basic_Types_Test.cpp + +Basic_Types_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bound_Ptr_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Bound_Ptr_Test + +Bound_Ptr_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bound_Ptr_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bound_Ptr_Test.cpp \ + Bound_Ptr_Test.h + +Bound_Ptr_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Buffer_Stream_Test.am + +noinst_PROGRAMS += Buffer_Stream_Test + +Buffer_Stream_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Buffer_Stream_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Buffer_Stream_Test.cpp + +Buffer_Stream_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_1576_Regression_Test.am + +noinst_PROGRAMS += Bug_1576_Regression_Test + +Bug_1576_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_1576_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_1576_Regression_Test.cpp + +Bug_1576_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_1890_Regression_Test.am + +noinst_PROGRAMS += Bug_1890_Regression_Test + +Bug_1890_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_1890_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_1890_Regression_Test.cpp + +Bug_1890_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_2368_Regression_Test.am + +noinst_PROGRAMS += Bug_2368_Regression_Test + +Bug_2368_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_2368_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_2368_Regression_Test.cpp + +Bug_2368_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_2497_Regression_Test.am + +noinst_PROGRAMS += Bug_2497_Regression_Test + +Bug_2497_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_2497_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_2497_Regression_Test.cpp + +Bug_2497_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_2540_Regression_Test.am + +noinst_PROGRAMS += Bug_2540_Regression_Test + +Bug_2540_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_2540_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_2540_Regression_Test.cpp + +Bug_2540_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_2653_Regression_Test.am + +noinst_PROGRAMS += Bug_2653_Regression_Test + +Bug_2653_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_2653_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_2653_Regression_Test.cpp + +Bug_2653_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Bug_2659_Regression_Test.am + +noinst_PROGRAMS += Bug_2659_Regression_Test + +Bug_2659_Regression_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Bug_2659_Regression_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Bug_2659_Regression_Test.cpp + +Bug_2659_Regression_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.CDR_Array_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += CDR_Array_Test + +CDR_Array_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +CDR_Array_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + CDR_Array_Test.cpp + +CDR_Array_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.CDR_File_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += CDR_File_Test + +CDR_File_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +CDR_File_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + CDR_File_Test.cpp \ + CE_fostream.cpp \ + CE_fostream.h + +CDR_File_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.CDR_Test.am + +noinst_PROGRAMS += CDR_Test + +CDR_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +CDR_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + CDR_Test.cpp + +CDR_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Cache_Map_Manager_Test.am + +noinst_PROGRAMS += Cache_Map_Manager_Test + +Cache_Map_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Cache_Map_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Cache_Map_Manager_Test.cpp \ + Cache_Map_Manager_Test.h + +Cache_Map_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Cached_Accept_Conn_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Cached_Accept_Conn_Test + +Cached_Accept_Conn_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Cached_Accept_Conn_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Cached_Accept_Conn_Test.cpp \ + Cached_Accept_Conn_Test.h + +Cached_Accept_Conn_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Cached_Allocator_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Cached_Allocator_Test + +Cached_Allocator_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Cached_Allocator_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Cached_Allocator_Test.cpp + +Cached_Allocator_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Cached_Conn_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Cached_Conn_Test + +Cached_Conn_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Cached_Conn_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Cached_Conn_Test.cpp \ + Cached_Conn_Test.h + +Cached_Conn_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Capabilities_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Capabilities_Test + +Capabilities_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Capabilities_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Capabilities_Test.cpp + +Capabilities_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Codecs_Test.am + +if BUILD_ACE_CODECS +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Codecs_Test + +Codecs_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Codecs_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Codecs_Test.cpp + +Codecs_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO +endif BUILD_ACE_CODECS + +## Makefile.Collection_Test.am + +noinst_PROGRAMS += Collection_Test + +Collection_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Collection_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Collection_Test.cpp \ + Collection_Test.h + +Collection_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Config_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Config_Test + +Config_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Config_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Config_Test.cpp \ + Config_Test.h + +Config_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Conn_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Conn_Test + +Conn_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Conn_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Conn_Test.cpp \ + Conn_Test.h + +Conn_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.DLL_Test_Parent_Lib.am + +noinst_LTLIBRARIES += libDLL_Test_Parent.la + +libDLL_Test_Parent_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DDLL_TEST_PARENT_BUILD_DLL + +libDLL_Test_Parent_la_SOURCES = \ + DLL_Test_Parent.cpp + +noinst_HEADERS += \ + DLL_Test_Parent.h \ + DLL_Test_Parent_Export.h \ + test_config.h + +## Makefile.DLL_Test_Lib.am + +noinst_LTLIBRARIES += libDLL_Test_Lib.la + +libDLL_Test_Lib_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_SVC_BUILD_DLL + +libDLL_Test_Lib_la_SOURCES = \ + DLL_Test_Impl.cpp + +noinst_HEADERS += \ + DLL_Test_Impl.h \ + test_config.h + +## Makefile.DLL_Test.am + +noinst_PROGRAMS += DLL_Test + +DLL_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +DLL_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + DLL_Test.cpp \ + DLL_Test.h + +DLL_Test_LDADD = \ + libDLL_Test_Lib.la \ + libDLL_Test_Parent.la \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.DLList_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += DLList_Test + +DLList_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +DLList_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + DLList_Test.cpp + +DLList_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Date_Time_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Date_Time_Test + +Date_Time_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Date_Time_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Date_Time_Test.cpp + +Date_Time_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Dev_Poll_Reactor_Test.am + +noinst_PROGRAMS += Dev_Poll_Reactor_Test + +Dev_Poll_Reactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Dev_Poll_Reactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Dev_Poll_Reactor_Test.cpp + +Dev_Poll_Reactor_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Dirent_Test.am + +noinst_PROGRAMS += Dirent_Test + +Dirent_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Dirent_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Dirent_Test.cpp + +Dirent_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Dynamic_Priority_Test.am + +noinst_PROGRAMS += Dynamic_Priority_Test + +Dynamic_Priority_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Dynamic_Priority_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Dynamic_Priority_Test.cpp + +Dynamic_Priority_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +noinst_PROGRAMS += Dynamic_Test + +Dynamic_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Dynamic_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Dynamic_Test.cpp + +Dynamic_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Enum_Interfaces_Test.am + +noinst_PROGRAMS += Enum_Interfaces_Test + +Enum_Interfaces_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Enum_Interfaces_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Enum_Interfaces_Test.cpp + +Enum_Interfaces_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Env_Value_Test.am + +noinst_PROGRAMS += Env_Value_Test + +Env_Value_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Env_Value_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Env_Value_Test.cpp + +Env_Value_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.FIFO_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += FIFO_Test + +FIFO_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +FIFO_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + FIFO_Test.cpp + +FIFO_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.FlReactor_Test.am + +if BUILD_ACE_FLREACTOR +if BUILD_FL +if BUILD_GL +if BUILD_X11 + +noinst_PROGRAMS += FlReactor_Test + +FlReactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + $(ACE_FLTK_CPPFLAGS) + +FlReactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + FlReactor_Test.cpp + +FlReactor_Test_LDFLAGS = \ + $(ACE_FLTK_LDFLAGS) + +FlReactor_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE_FlReactor.la \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + $(ACE_FLTK_LIBS) + +endif BUILD_X11 +endif BUILD_GL +endif BUILD_FL +endif BUILD_ACE_FLREACTOR + +## Makefile.Framework_Component_DLL.am + +noinst_LTLIBRARIES += libFramework_Component_DLL.la + +libFramework_Component_DLL_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DFRAMEWORK_COMPONENT_DLL_BUILD_DLL + +libFramework_Component_DLL_la_SOURCES = \ + Framework_Component_DLL.cpp + +noinst_HEADERS += \ + Framework_Component_DLL.h \ + Framework_Component_DLL_Export.h + +## Makefile.Framework_Component_Test.am + +noinst_PROGRAMS += Framework_Component_Test + +Framework_Component_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Framework_Component_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Framework_Component_Test.cpp \ + Framework_Component_Test.h + +Framework_Component_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Future_Set_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Future_Set_Test + +Future_Set_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Future_Set_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Future_Set_Test.cpp + +Future_Set_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Future_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Future_Test + +Future_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Future_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Future_Test.cpp + +Future_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Get_Opt_Test.am + +noinst_PROGRAMS += Get_Opt_Test + +Get_Opt_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Get_Opt_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Get_Opt_Test.cpp + +Get_Opt_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Handle_Set_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Handle_Set_Test + +Handle_Set_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Handle_Set_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Handle_Set_Test.cpp + +Handle_Set_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Hash_Map_Bucket_Iterator_Test.am + +noinst_PROGRAMS += Hash_Map_Bucket_Iterator_Test + +Hash_Map_Bucket_Iterator_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Hash_Map_Bucket_Iterator_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Hash_Map_Bucket_Iterator_Test.cpp + +Hash_Map_Bucket_Iterator_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Hash_Map_Manager_Test.am + +noinst_PROGRAMS += Hash_Map_Manager_Test + +Hash_Map_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Hash_Map_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Hash_Map_Manager_Test.cpp + +Hash_Map_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Hash_Multi_Map_Manager_Test.am + +noinst_PROGRAMS += Hash_Multi_Map_Manager_Test + +Hash_Multi_Map_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Hash_Multi_Map_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Hash_Multi_Map_Manager_Test.cpp + +Hash_Multi_Map_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.High_Res_Timer_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += High_Res_Timer_Test + +High_Res_Timer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +High_Res_Timer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + High_Res_Timer_Test.cpp + +High_Res_Timer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.INET_Addr_Test.am + +noinst_PROGRAMS += INET_Addr_Test + +INET_Addr_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +INET_Addr_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + INET_Addr_Test.cpp + +INET_Addr_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.INET_Addr_Test_IPV6.am + +noinst_PROGRAMS += INET_Addr_Test_IPV6 + +INET_Addr_Test_IPV6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +INET_Addr_Test_IPV6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + INET_Addr_Test_IPV6.cpp + +INET_Addr_Test_IPV6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.IOStream_Test.am + +noinst_PROGRAMS += IOStream_Test + +IOStream_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +IOStream_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + IOStream_Test.cpp + +IOStream_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Integer_Truncate_Test.am + +noinst_PROGRAMS += Integer_Truncate_Test + +Integer_Truncate_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Integer_Truncate_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Integer_Truncate_Test.cpp + +Integer_Truncate_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Lazy_Map_Manager_Test.am + +noinst_PROGRAMS += Lazy_Map_Manager_Test + +Lazy_Map_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Lazy_Map_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Lazy_Map_Manager_Test.cpp + +Lazy_Map_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Library_Unload.am + +if !BUILD_WINCE + +noinst_PROGRAMS += UnloadLibACE + +UnloadLibACE_SOURCES = \ + Unload_libACE.cpp \ + ACE_Init_Test.h \ + ACE_Init_TestDlg.h \ + ACE_Init_Test_Resource.h \ + ACE_Init_Test_StdAfx.h \ + Bound_Ptr_Test.h \ + CE_fostream.h \ + Cache_Map_Manager_Test.h \ + Cached_Accept_Conn_Test.h \ + Cached_Conn_Test.h \ + Collection_Test.h \ + Config_Test.h \ + Conn_Test.h \ + DLL_Test.h \ + DLL_Test_Impl.h \ + DLL_Test_Parent.h \ + DLL_Test_Parent_Export.h \ + Framework_Component_DLL.h \ + Framework_Component_DLL_Export.h \ + Framework_Component_Test.h \ + MEM_Stream_Test.h \ + MT_Reactor_Timer_Test.h \ + Malloc_Test.h \ + Map_Test.h \ + Max_Default_Port_Test.h \ + Message_Queue_Test_Ex.h \ + Network_Adapters_Test.h \ + NonBlocking_Conn_Test.h \ + Priority_Reactor_Test.h \ + Proactor_Test.h \ + Process_Strategy_Test.h \ + QtReactor_Test.h \ + RB_Tree_Test.h \ + Reactor_Performance_Test.h \ + Refcounted_Auto_Ptr_Test.h \ + Service_Config_DLL.h \ + Service_Config_DLL_Export.h \ + TP_Reactor_Test.h \ + TSS_Test_Errno.h \ + Task_Ex_Test.h \ + Test_Output_Export.h \ + Thread_Pool_Reactor_Resume_Test.h \ + Thread_Pool_Reactor_Test.h \ + Upgradable_RW_Test.h \ + test_config.h + +endif !BUILD_WINCE + +## Makefile.Log_Msg_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Log_Msg_Test + +Log_Msg_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Log_Msg_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Log_Msg_Test.cpp + +Log_Msg_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + + +## Makefile.Log_Msg_Backend_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Log_Msg_Backend_Test + +Log_Msg_Backend_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Log_Msg_Backend_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Log_Msg_Backend_Test.cpp + +Log_Msg_Backend_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Logging_Strategy_Test.am + +noinst_PROGRAMS += Logging_Strategy_Test + +Logging_Strategy_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Logging_Strategy_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Logging_Strategy_Test.cpp + +Logging_Strategy_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.MEM_Stream_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += MEM_Stream_Test + +MEM_Stream_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MEM_Stream_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MEM_Stream_Test.cpp \ + MEM_Stream_Test.h + +MEM_Stream_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.MM_Shared_Memory_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += MM_Shared_Memory_Test + +MM_Shared_Memory_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MM_Shared_Memory_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MM_Shared_Memory_Test.cpp + +MM_Shared_Memory_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.MT_Reactor_Timer_Test.am + +noinst_PROGRAMS += MT_Reactor_Timer_Test + +MT_Reactor_Timer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MT_Reactor_Timer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MT_Reactor_Timer_Test.cpp \ + MT_Reactor_Timer_Test.h + +MT_Reactor_Timer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.MT_Reactor_Upcall_Test.am + +noinst_PROGRAMS += MT_Reactor_Upcall_Test + +MT_Reactor_Upcall_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MT_Reactor_Upcall_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MT_Reactor_Upcall_Test.cpp + +MT_Reactor_Upcall_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.MT_Ref_Counted_Event_Handler_Test.am + +noinst_PROGRAMS += MT_Reference_Counted_Event_Handler_Test + +MT_Reference_Counted_Event_Handler_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MT_Reference_Counted_Event_Handler_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MT_Reference_Counted_Event_Handler_Test.cpp + +MT_Reference_Counted_Event_Handler_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.MT_Reference_Counted_Notify_Test.am + +noinst_PROGRAMS += MT_Reference_Counted_Notify_Test + +MT_Reference_Counted_Notify_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MT_Reference_Counted_Notify_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MT_Reference_Counted_Notify_Test.cpp + +MT_Reference_Counted_Notify_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.MT_SOCK_Test.am + +noinst_PROGRAMS += MT_SOCK_Test + +MT_SOCK_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +MT_SOCK_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + MT_SOCK_Test.cpp + +MT_SOCK_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Malloc_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Malloc_Test + +Malloc_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Malloc_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Malloc_Test.cpp \ + Malloc_Test.h + +Malloc_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Manual_Event_Test.am + +noinst_PROGRAMS += Manual_Event_Test + +Manual_Event_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Manual_Event_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Manual_Event_Test.cpp + +Manual_Event_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Map_Manager_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Map_Manager_Test + +Map_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Map_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Map_Manager_Test.cpp + +Map_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Map_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Map_Test + +Map_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Map_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Map_Test.cpp \ + Map_Test.h + +Map_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Max_Default_Port_Test.am + +noinst_PROGRAMS += Max_Default_Port_Test + +Max_Default_Port_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Max_Default_Port_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Max_Default_Port_Test.cpp \ + Max_Default_Port_Test.h + +Max_Default_Port_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Max_Default_Port_Test_IPV6.am + +noinst_PROGRAMS += Max_Default_Port_Test_IPV6 + +Max_Default_Port_Test_IPV6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Max_Default_Port_Test_IPV6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Max_Default_Port_Test_IPV6.cpp + +Max_Default_Port_Test_IPV6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Mem_Map_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Mem_Map_Test + +Mem_Map_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Mem_Map_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Mem_Map_Test.cpp + +Mem_Map_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Memcpy_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Memcpy_Test + +Memcpy_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Memcpy_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Memcpy_Test.cpp + +Memcpy_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Message_Block_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Message_Block_Test + +Message_Block_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Message_Block_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Message_Block_Test.cpp + +Message_Block_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Message_Queue_Notifications_Test.am + +noinst_PROGRAMS += Message_Queue_Notifications_Test + +Message_Queue_Notifications_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Message_Queue_Notifications_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Message_Queue_Notifications_Test.cpp + +Message_Queue_Notifications_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Message_Queue_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Message_Queue_Test + +Message_Queue_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Message_Queue_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Message_Queue_Test.cpp + +Message_Queue_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Message_Queue_Test_Ex.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Message_Queue_Test_Ex + +Message_Queue_Test_Ex_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Message_Queue_Test_Ex_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Message_Queue_Test_Ex.cpp \ + Message_Queue_Test_Ex.h + +Message_Queue_Test_Ex_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Multicast_Test.am + +noinst_PROGRAMS += Multicast_Test + +Multicast_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Multicast_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Multicast_Test.cpp + +Multicast_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Multicast_Test_IPV6.am + +noinst_PROGRAMS += Multicast_Test_IPV6 + +Multicast_Test_IPV6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Multicast_Test_IPV6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Multicast_Test_IPV6.cpp + +Multicast_Test_IPV6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Multihomed_INET_Addr_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Multihomed_INET_Addr_Test + +Multihomed_INET_Addr_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Multihomed_INET_Addr_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Multihomed_INET_Addr_Test.cpp + +Multihomed_INET_Addr_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Multihomed_INET_Addr_Test_IPV6.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Multihomed_INET_Addr_Test_IPV6 + +Multihomed_INET_Addr_Test_IPV6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Multihomed_INET_Addr_Test_IPV6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Multihomed_INET_Addr_Test_IPV6.cpp + +Multihomed_INET_Addr_Test_IPV6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Naming_Test.am + +if BUILD_ACE_OTHER +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Naming_Test + +Naming_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Naming_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Naming_Test.cpp + +Naming_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO +endif BUILD_ACE_OTHER + +## Makefile.Network_Adapters_Test.am + +noinst_PROGRAMS += Network_Adapters_Test + +Network_Adapters_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Network_Adapters_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Network_Adapters_Test.cpp \ + Network_Adapters_Test.h + +Network_Adapters_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.New_Fail_Test.am + +noinst_PROGRAMS += New_Fail_Test + +New_Fail_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +New_Fail_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + New_Fail_Test.cpp + +New_Fail_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.NonBlocking_Conn_Test.am + +noinst_PROGRAMS += NonBlocking_Conn_Test + +NonBlocking_Conn_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +NonBlocking_Conn_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + NonBlocking_Conn_Test.cpp \ + NonBlocking_Conn_Test.h + +NonBlocking_Conn_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Notify_Performance_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Notify_Performance_Test + +Notify_Performance_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Notify_Performance_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Notify_Performance_Test.cpp + +Notify_Performance_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.OS_Test.am + +noinst_PROGRAMS += OS_Test + +OS_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +OS_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + OS_Test.cpp + +OS_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Object_Manager_Test.am + +noinst_PROGRAMS += Object_Manager_Test + +Object_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Object_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Object_Manager_Test.cpp + +Object_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Obstack_Test.am + +noinst_PROGRAMS += Obstack_Test + +Obstack_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Obstack_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Obstack_Test.cpp + +Obstack_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.OrdMultiSet_Test.am + +noinst_PROGRAMS += OrdMultiSet_Test + +OrdMultiSet_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +OrdMultiSet_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + OrdMultiSet_Test.cpp + +OrdMultiSet_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Pipe_Test.am + +noinst_PROGRAMS += Pipe_Test + +Pipe_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Pipe_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Pipe_Test.cpp + +Pipe_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Priority_Buffer_Test.am + +noinst_PROGRAMS += Priority_Buffer_Test + +Priority_Buffer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Priority_Buffer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Priority_Buffer_Test.cpp + +Priority_Buffer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Priority_Reactor_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Priority_Reactor_Test + +Priority_Reactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Priority_Reactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Priority_Reactor_Test.cpp \ + Priority_Reactor_Test.h + +Priority_Reactor_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Priority_Task_Test.am + +noinst_PROGRAMS += Priority_Task_Test + +Priority_Task_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Priority_Task_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Priority_Task_Test.cpp + +Priority_Task_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Proactor_Scatter_Gather_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Proactor_Scatter_Gather_Test + +Proactor_Scatter_Gather_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Proactor_Scatter_Gather_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Proactor_Scatter_Gather_Test.cpp + +Proactor_Scatter_Gather_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Proactor_Test + +Proactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Proactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Proactor_Test.cpp \ + Proactor_Test.h + +Proactor_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Test_IPV6.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Proactor_Test_IPV6 + +Proactor_Test_IPV6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Proactor_Test_IPV6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Proactor_Test_IPV6.cpp + +Proactor_Test_IPV6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Timer_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Proactor_Timer_Test + +Proactor_Timer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Proactor_Timer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Proactor_Timer_Test.cpp + +Proactor_Timer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Process_Manager_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Process_Manager_Test + +Process_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Process_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Process_Manager_Test.cpp + +Process_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Process_Manual_Event_Test.am + +noinst_PROGRAMS += Process_Manual_Event_Test + +Process_Manual_Event_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Process_Manual_Event_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Process_Manual_Event_Test.cpp + +Process_Manual_Event_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Process_Mutex_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Process_Mutex_Test + +Process_Mutex_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Process_Mutex_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Process_Mutex_Test.cpp + +Process_Mutex_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Process_Semaphore_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Process_Semaphore_Test + +Process_Semaphore_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Process_Semaphore_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Process_Semaphore_Test.cpp + +Process_Semaphore_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Process_Strategy_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Process_Strategy_Test + +Process_Strategy_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Process_Strategy_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Process_Strategy_Test.cpp \ + Process_Strategy_Test.h + +Process_Strategy_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.QtReactor_Test.am + +if BUILD_ACE_QTREACTOR +if BUILD_QT + +BUILT_SOURCES = \ + QtReactor_Test_moc.cpp + +CLEANFILES = \ + QtReactor_Test_moc.cpp + +QtReactor_Test_moc.cpp: $(srcdir)/QtReactor_Test.h + $(QTDIR)/bin/moc $(srcdir)/QtReactor_Test.h -o QtReactor_Test_moc.cpp + +noinst_PROGRAMS += QtReactor_Test + +QtReactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + $(ACE_QT_CPPFLAGS) + +QtReactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + QtReactor_Test.cpp \ + QtReactor_Test_moc.cpp \ + QtReactor_Test.h + +QtReactor_Test_LDFLAGS = \ + $(ACE_QT_LDFLAGS) + +QtReactor_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE_QtReactor.la \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + $(ACE_QT_LIBS) + +endif BUILD_QT +endif BUILD_ACE_QTREACTOR + +## Makefile.RB_Tree_Test.am + +noinst_PROGRAMS += RB_Tree_Test + +RB_Tree_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +RB_Tree_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + RB_Tree_Test.cpp \ + RB_Tree_Test.h + +RB_Tree_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Dispatch_Order_Test.am + +noinst_PROGRAMS += Reactor_Dispatch_Order_Test + +Reactor_Dispatch_Order_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Dispatch_Order_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Dispatch_Order_Test.cpp + +Reactor_Dispatch_Order_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Exceptions_Test.am + +noinst_PROGRAMS += Reactor_Exceptions_Test + +Reactor_Exceptions_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Exceptions_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Exceptions_Test.cpp + +Reactor_Exceptions_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Notification_Queue_Test.am + +noinst_PROGRAMS += Reactor_Notification_Queue_Test + +Reactor_Notification_Queue_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Notification_Queue_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Notification_Queue_Test.cpp + +Reactor_Notification_Queue_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Notify_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Reactor_Notify_Test + +Reactor_Notify_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Notify_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Notify_Test.cpp + +Reactor_Notify_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Reactor_Performance_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Reactor_Performance_Test + +Reactor_Performance_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Performance_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Performance_Test.cpp \ + Reactor_Performance_Test.h + +Reactor_Performance_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Reactor_Registration_Test.am + +noinst_PROGRAMS += Reactor_Registration_Test + +Reactor_Registration_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Registration_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Registration_Test.cpp + +Reactor_Registration_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Remove_Resume_Test.am + +noinst_PROGRAMS += Reactor_Remove_Resume_Test + +Reactor_Remove_Resume_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Remove_Resume_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Remove_Resume_Test.cpp + +Reactor_Remove_Resume_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Timer_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Reactor_Timer_Test + +Reactor_Timer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactor_Timer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactor_Timer_Test.cpp + +Reactor_Timer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Reactors_Test.am + +noinst_PROGRAMS += Reactors_Test + +Reactors_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reactors_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reactors_Test.cpp + +Reactors_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reader_Writer_Test.am + +noinst_PROGRAMS += Reader_Writer_Test + +Reader_Writer_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reader_Writer_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reader_Writer_Test.cpp + +Reader_Writer_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Recursive_Condition_Bug_Test.am + +noinst_PROGRAMS += Recursive_Condition_Bug_Test + +Recursive_Condition_Bug_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Recursive_Condition_Bug_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Recursive_Condition_Bug_Test.cpp + +Recursive_Condition_Bug_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Recursive_Condition_Test.am + +noinst_PROGRAMS += Recursive_Condition_Test + +Recursive_Condition_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Recursive_Condition_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Recursive_Condition_Test.cpp + +Recursive_Condition_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Recursive_Mutex_Test.am + +noinst_PROGRAMS += Recursive_Mutex_Test + +Recursive_Mutex_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Recursive_Mutex_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Recursive_Mutex_Test.cpp + +Recursive_Mutex_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Refcounted_Auto_Ptr_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Refcounted_Auto_Ptr_Test + +Refcounted_Auto_Ptr_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Refcounted_Auto_Ptr_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Refcounted_Auto_Ptr_Test.cpp \ + Refcounted_Auto_Ptr_Test.h + +Refcounted_Auto_Ptr_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Reference_Counted_Event_Handler_Test.am + +noinst_PROGRAMS += Reference_Counted_Event_Handler_Test + +Reference_Counted_Event_Handler_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reference_Counted_Event_Handler_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reference_Counted_Event_Handler_Test.cpp + +Reference_Counted_Event_Handler_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reverse_Lock_Test.am + +noinst_PROGRAMS += Reverse_Lock_Test + +Reverse_Lock_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Reverse_Lock_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Reverse_Lock_Test.cpp + +Reverse_Lock_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SOCK_Connector_Test.am + +noinst_PROGRAMS += SOCK_Connector_Test + +SOCK_Connector_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Connector_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Connector_Test.cpp + +SOCK_Connector_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SOCK_Dgram_Bcast_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_Dgram_Bcast_Test + +SOCK_Dgram_Bcast_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Dgram_Bcast_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Dgram_Bcast_Test.cpp + +SOCK_Dgram_Bcast_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SOCK_Dgram_Test.am + +noinst_PROGRAMS += SOCK_Dgram_Test + +SOCK_Dgram_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Dgram_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Dgram_Test.cpp + +SOCK_Dgram_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SOCK_Netlink_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_Netlink_Test + +SOCK_Netlink_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Netlink_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Netlink_Test.cpp + +SOCK_Netlink_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SOCK_SEQPACK_SCTP_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_SEQPACK_SCTP_Test + +SOCK_SEQPACK_SCTP_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_SEQPACK_SCTP_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_SEQPACK_SCTP_Test.cpp + +SOCK_SEQPACK_SCTP_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SOCK_Send_Recv_Test.am + +noinst_PROGRAMS += SOCK_Send_Recv_Test + +SOCK_Send_Recv_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Send_Recv_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Send_Recv_Test.cpp + +SOCK_Send_Recv_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SOCK_Send_Recv_Test_IPV6.am + +noinst_PROGRAMS += SOCK_Send_Recv_Test_IPV6 + +SOCK_Send_Recv_Test_IPV6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Send_Recv_Test_IPV6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Send_Recv_Test_IPV6.cpp + +SOCK_Send_Recv_Test_IPV6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SOCK_Test.am + +noinst_PROGRAMS += SOCK_Test + +SOCK_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Test.cpp + +SOCK_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SOCK_Test_IPv6.am + +noinst_PROGRAMS += SOCK_Test_IPv6 + +SOCK_Test_IPv6_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_Test_IPv6_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SOCK_Test_IPv6.cpp + +SOCK_Test_IPv6_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SPIPE_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SPIPE_Test + +SPIPE_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SPIPE_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SPIPE_Test.cpp + +SPIPE_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SString_Test.am + +noinst_PROGRAMS += SString_Test + +SString_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SString_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SString_Test.cpp + +SString_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.SV_Shared_Memory_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SV_Shared_Memory_Test + +SV_Shared_Memory_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SV_Shared_Memory_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SV_Shared_Memory_Test.cpp + +SV_Shared_Memory_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Semaphore_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Semaphore_Test + +Semaphore_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Semaphore_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Semaphore_Test.cpp + +Semaphore_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Sendfile_Test.am + +noinst_PROGRAMS += Sendfile_Test + +Sendfile_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Sendfile_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Sendfile_Test.cpp + +Sendfile_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Service_Config_DLL.am + +noinst_LTLIBRARIES += libService_Config_DLL.la + +libService_Config_DLL_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DSERVICE_CONFIG_DLL_BUILD_DLL + +libService_Config_DLL_la_SOURCES = \ + Service_Config_DLL.cpp + +noinst_HEADERS += \ + Service_Config_DLL.h \ + Service_Config_DLL_Export.h + +## Makefile.Service_Config_Test.am + +noinst_PROGRAMS += Service_Config_Test + +Service_Config_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Service_Config_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Service_Config_Test.cpp + +Service_Config_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Signal_Test.am + +noinst_PROGRAMS += Signal_Test + +Signal_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Signal_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Signal_Test.cpp + +Signal_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Sigset_Ops_Test.am + +noinst_PROGRAMS += Sigset_Ops_Test + +Sigset_Ops_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Sigset_Ops_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Sigset_Ops_Test.cpp + +Sigset_Ops_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Simple_Message_Block_Test.am + +noinst_PROGRAMS += Simple_Message_Block_Test + +Simple_Message_Block_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Simple_Message_Block_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Simple_Message_Block_Test.cpp + +Simple_Message_Block_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Svc_Handler_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Svc_Handler_Test + +Svc_Handler_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Svc_Handler_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Svc_Handler_Test.cpp + +Svc_Handler_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.TP_Reactor_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += TP_Reactor_Test + +TP_Reactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +TP_Reactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + TP_Reactor_Test.cpp \ + TP_Reactor_Test.h + +TP_Reactor_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.TSS_Static_Test.am + +noinst_PROGRAMS += TSS_Static_Test + +TSS_Static_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +TSS_Static_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + TSS_Static_Test.cpp + +TSS_Static_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.TSS_Test.am + +noinst_PROGRAMS += TSS_Test + +TSS_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +TSS_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + TSS_Test.cpp + +TSS_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Task_Ex_Test.am + +noinst_PROGRAMS += Task_Ex_Test + +Task_Ex_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Task_Ex_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Task_Ex_Test.cpp \ + Task_Ex_Test.h + +Task_Ex_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Task_Test.am + +noinst_PROGRAMS += Task_Test + +Task_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Task_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Task_Test.cpp + +Task_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Thread_Manager_Test.am + +noinst_PROGRAMS += Thread_Manager_Test + +Thread_Manager_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Thread_Manager_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Thread_Manager_Test.cpp + +Thread_Manager_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Thread_Mutex_Test.am + +noinst_PROGRAMS += Thread_Mutex_Test + +Thread_Mutex_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Thread_Mutex_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Thread_Mutex_Test.cpp + +Thread_Mutex_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Thread_Pool_Reactor_Resume_Test.am + +if BUILD_ACE_OTHER + +noinst_PROGRAMS += Thread_Pool_Reactor_Resume_Test + +Thread_Pool_Reactor_Resume_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Thread_Pool_Reactor_Resume_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Thread_Pool_Reactor_Resume_Test.cpp \ + Thread_Pool_Reactor_Resume_Test.h + +Thread_Pool_Reactor_Resume_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_ACE_OTHER + +## Makefile.Thread_Pool_Reactor_Test.am + +if BUILD_ACE_OTHER + +noinst_PROGRAMS += Thread_Pool_Reactor_Test + +Thread_Pool_Reactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Thread_Pool_Reactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Thread_Pool_Reactor_Test.cpp \ + Thread_Pool_Reactor_Test.h + +Thread_Pool_Reactor_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_ACE_OTHER + +## Makefile.Thread_Pool_Test.am + +noinst_PROGRAMS += Thread_Pool_Test + +Thread_Pool_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Thread_Pool_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Thread_Pool_Test.cpp + +Thread_Pool_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Time_Service_Test.am + +noinst_PROGRAMS += Time_Service_Test + +Time_Service_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Time_Service_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Time_Service_Test.cpp + +Time_Service_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Time_Value_Test.am + +noinst_PROGRAMS += Time_Value_Test + +Time_Value_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Time_Value_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Time_Value_Test.cpp + +Time_Value_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Timeprobe_Test.am + +noinst_PROGRAMS += Timeprobe_Test + +Timeprobe_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Timeprobe_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Timeprobe_Test.cpp + +Timeprobe_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Timer_Cancellation_Test.am + +noinst_PROGRAMS += Timer_Cancellation_Test + +Timer_Cancellation_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Timer_Cancellation_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Timer_Cancellation_Test.cpp + +Timer_Cancellation_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Timer_Queue_Reference_Counting_Test.am + +noinst_PROGRAMS += Timer_Queue_Reference_Counting_Test + +Timer_Queue_Reference_Counting_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Timer_Queue_Reference_Counting_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Timer_Queue_Reference_Counting_Test.cpp + +Timer_Queue_Reference_Counting_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Timer_Queue_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Timer_Queue_Test + +Timer_Queue_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Timer_Queue_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Timer_Queue_Test.cpp + +Timer_Queue_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.TkReactor_Test.am + +if BUILD_ACE_TKREACTOR +if BUILD_TK + +noinst_PROGRAMS += TkReactor_Test + +TkReactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + $(ACE_TK_CPPFLAGS) \ + $(ACE_TCL_CPPFLAGS) + +TkReactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + TkReactor_Test.cpp + +TkReactor_Test_LDFLAGS = \ + $(ACE_TK_LDFLAGS) $(ACE_TCL_LDFLAGS) + +TkReactor_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE_TkReactor.la \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + $(ACE_TK_LIBS) \ + $(ACE_TCL_LIBS) + +endif BUILD_TK +endif BUILD_ACE_TKREACTOR + +## Makefile.Token_Strategy_Test.am + +noinst_PROGRAMS += Token_Strategy_Test + +Token_Strategy_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Token_Strategy_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Token_Strategy_Test.cpp + +Token_Strategy_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Tokens_Test.am + +if BUILD_ACE_TOKEN + +noinst_PROGRAMS += Tokens_Test + +Tokens_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Tokens_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Tokens_Test.cpp + +Tokens_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_ACE_TOKEN + +## Makefile.UPIPE_SAP_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += UPIPE_SAP_Test + +UPIPE_SAP_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +UPIPE_SAP_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + UPIPE_SAP_Test.cpp + +UPIPE_SAP_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.UUID_Test.am + +if BUILD_ACE_UUID +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += UUID_Test + +UUID_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +UUID_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + UUID_Test.cpp + +UUID_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO +endif BUILD_ACE_UUID + +## Makefile.Unbounded_Set_Test.am + +noinst_PROGRAMS += Unbounded_Set_Test + +Unbounded_Set_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Unbounded_Set_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Unbounded_Set_Test.cpp + +Unbounded_Set_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Upgradable_RW_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += Upgradable_RW_Test + +Upgradable_RW_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Upgradable_RW_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Upgradable_RW_Test.cpp \ + Upgradable_RW_Test.h + +Upgradable_RW_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Vector_Test.am + +noinst_PROGRAMS += Vector_Test + +Vector_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +Vector_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Vector_Test.cpp + +Vector_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.WFMO_Reactor_Test.am + +noinst_PROGRAMS += WFMO_Reactor_Test + +WFMO_Reactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +WFMO_Reactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + WFMO_Reactor_Test.cpp + +WFMO_Reactor_Test_LDADD = \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.XtAthenaReactor_Test.am + +if BUILD_ACE_XTREACTOR +if BUILD_ATHENA +if BUILD_X11 +if BUILD_XT + +noinst_PROGRAMS += XtAthenaReactor_Test + +XtAthenaReactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + $(ACE_X11_CPPFLAGS) \ + $(ACE_XT_CPPFLAGS) + +XtAthenaReactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + XtAthenaReactor_Test.cpp + +XtAthenaReactor_Test_LDFLAGS = \ + $(ACE_X11_LDFLAGS) $(ACE_XT_LDFLAGS) + +XtAthenaReactor_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE_XtReactor.la \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + -lXaw \ + $(ACE_XT_LIBS) \ + $(ACE_X11_LIBS) + +endif BUILD_XT +endif BUILD_X11 +endif BUILD_ATHENA +endif BUILD_ACE_XTREACTOR + +## Makefile.XtMotifReactor_Test.am + +if BUILD_ACE_XTREACTOR +if BUILD_MOTIF +if BUILD_X11 +if BUILD_XT + +noinst_PROGRAMS += XtMotifReactor_Test + +XtMotifReactor_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + $(ACE_X11_CPPFLAGS) \ + $(ACE_XT_CPPFLAGS) + +XtMotifReactor_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + XtMotifReactor_Test.cpp + +XtMotifReactor_Test_LDFLAGS = \ + $(ACE_X11_LDFLAGS) $(ACE_XT_LDFLAGS) + +XtMotifReactor_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE_XtReactor.la \ + libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + -lXm \ + $(ACE_XT_LIBS) \ + $(ACE_X11_LIBS) + +endif BUILD_XT +endif BUILD_X11 +endif BUILD_MOTIF +endif BUILD_ACE_XTREACTOR + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/tests/Malloc_Test.cpp b/ACE/tests/Malloc_Test.cpp new file mode 100644 index 00000000000..14a07aa19c2 --- /dev/null +++ b/ACE/tests/Malloc_Test.cpp @@ -0,0 +1,437 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Malloc_Test.cpp +// +// = DESCRIPTION +// This is a test of the position-independent <ACE_Malloc> memory +// manager using the <ACE_MMAP_Memory_Pool> and <ACE_Process_Mutex>. +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "Malloc_Test.h" + +#include "ace/Malloc_T.h" +#include "ace/MMAP_Memory_Pool.h" +#include "ace/Process.h" +#include "ace/Auto_Ptr.h" +#include "ace/Process_Mutex.h" +#include "ace/PI_Malloc.h" +#include "ace/RW_Thread_Mutex.h" +#include "ace/Time_Value.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Malloc_Test, "Malloc_Test.cpp,v 4.22 1999/12/13 22:24:42 nanbor Exp") + +#if defined (ACE_HAS_PROCESS_SPAWN) + +#if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) +typedef ACE_Malloc_T<ACE_MMAP_MEMORY_POOL, ACE_Process_Mutex, ACE_PI_Control_Block> MALLOC; +#else +typedef ACE_Malloc<ACE_MMAP_MEMORY_POOL, ACE_Process_Mutex> MALLOC; +#endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1 */ +#define MMAP_FILENAME ACE_TEXT ("test_file") +#define MUTEX_NAME ACE_TEXT ("test_lock") + +#if !defined (linux) && !defined (ACE_OPENVMS) \ + && !(defined (ACE_WIN32) \ + && (defined (ghs) || defined (__MINGW32__) )) \ + && !(defined (__OpenBSD__) && defined (ACE_HAS_PTHREADS)) +#define ACE_TEST_REMAP_ON_FAULT +// Linux seems to have problem when calling mmap from the signal handler. +// The Green Hills Native x86 compiler does not support structural exceptions. +// Mingw's gcc does not support structural exceptions. +// Win9x doesn't support remaps. +// OpenBSD causes this test to hang in the child when pthreads are enabled. +// On these plarforms, we make sure the remapping will never occur. +#endif /* linux && Win32 GHS*/ + +#if defined (ACE_WIN32) +// When looking for the file to execute a process on Win32, the directory from +// containing the parent process file is searched first. Since certain Win32 +// configurations (e.g. Borland C++Builder) put the output files in a different +// directory we will use this feature rather than specifying '.\'. +# define EXE_LOCATION ACE_TEXT ("") +#else +# define EXE_LOCATION ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR +#endif /*ACE_WIN32*/ + +// Parents <ACE_Malloc> base address in shared memory. +static const void *PARENT_BASE_ADDR = ACE_DEFAULT_BASE_ADDR; + +// If the platform supports position-independent malloc, choose +// another base address that's 1M higher so that <ACE_Malloc> will be +// mapped into a different address in the child's virtual memory. +// Note that on HP-UX on PA-RISC hardware, a single range of a file +// cannot be mapped into multiple virtual address ranges, even across +// processes. So, though the whole PI pointer thing is tested here, +// it isn't actually using multiple address ranges. + +#if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1 && !defined (HPUX)) +# define CHILD_ADDR_DELTA (1024*1024) +#else +# define CHILD_ADDR_DELTA 0 +#endif /* CHILD_ADDR_DELTA */ + +static const void *CHILD_BASE_ADDR = + (const void *)(CHILD_ADDR_DELTA + ACE_DEFAULT_BASE_ADDR); + +// Shared memory allocator. Hide the allocator inside this function +// so that it doesn't get constructed until after the +// <ACE_Object_Manager> gets constructed, even with +// <ACE_HAS_NONSTATIC_OBJECT_MANAGER>. + +static MALLOC * +myallocator (const void *base_addr = 0) +{ + static auto_ptr<MALLOC> static_allocator; + + if (static_allocator.get () == 0) + { + +#if defined (ACE_HAS_WINCE) || defined (ACE_OPENVMS) + // WinCE cannot do fixed base, ever. + ACE_MMAP_Memory_Pool_Options options + (0, + ACE_MMAP_Memory_Pool_Options::NEVER_FIXED); +#else + ACE_MMAP_Memory_Pool_Options options (base_addr); +#endif /* ACE_HAS_WINCE */ + +#if !defined (ACE_TEST_REMAP_ON_FAULT) + options.minimum_bytes_ = 512 * 1024; +#endif /* ACE_TEST_REMAP_ON_FAULT */ + + MALLOC *ptr = new MALLOC (MMAP_FILENAME, + MUTEX_NAME, + &options); + ACE_AUTO_PTR_RESET(static_allocator, ptr, MALLOC); + } + return static_allocator.get (); +} + +static void +init_test (const void *base_addr = 0) +{ + // Cleanup the MMAP file so we won't trip over the leftover mmap + // file from the previous crash. +#if defined (ACE_HAS_WINCE) || defined (ACE_OPENVMS) + // WinCE cannot do fixed base, ever. + ACE_MMAP_Memory_Pool_Options options + (0, + ACE_MMAP_Memory_Pool_Options::NEVER_FIXED); +#else + ACE_MMAP_Memory_Pool_Options options (base_addr); +#endif /* ACE_HAS_WINCE */ + //FUZZ: disable check_for_lack_ACE_OS + ACE_MMAP_Memory_Pool mmap (MMAP_FILENAME, &options); + //FUZZ: enable check_for_lack_ACE_OS + + size_t rbyte = 0; + int ft = 0; + mmap.init_acquire (1024, rbyte, ft); + mmap.release (); +} + +static Test_Data * +initialize (MALLOC *allocator) +{ + double *temp = 0; + ACE_ALLOCATOR_RETURN (temp, + (double *) allocator->malloc (sizeof (double)), + 0); + // Make sure that doubles work! + *temp = 5.0; + allocator->free (temp); + + void *ptr = 0; + ACE_ALLOCATOR_RETURN (ptr, + allocator->malloc (sizeof (Test_Data)), + 0); + Test_Data *data1 = new (ptr) Test_Data; + + data1->i1_ = 111; + data1->i2_ = 222; + data1->i3_ = 333; + data1->d1_ = 87.5; + + void *gap = 0; + ACE_ALLOCATOR_RETURN (gap, + allocator->malloc (sizeof (256)), + 0); + allocator->free (gap); + + + ACE_ALLOCATOR_RETURN (ptr, + allocator->malloc (sizeof (Test_Data)), + 0); + Test_Data *data2 = new (ptr) Test_Data; + + data1->next_ = 0; + data2->next_ = data1; + data2->i1_ = -111; + data2->i2_ = -222; + data2->i3_ = -333; + data2->d1_ = 77.34; + + // Test in shared memory using long (array/pointer) + ACE_ALLOCATOR_RETURN (ptr, + allocator->malloc (sizeof (Long_Test)), + 0); + Long_Test *lt = new (ptr) Long_Test; + + lt->array_[0] = 1000; + lt->array_[1] = 1001; + lt->array_[2] = 1002; + lt->array_[3] = 1003; + lt->array_[4] = 1004; + lt->bpl_ = lt->array_; + + data1->long_test_= lt; + + long long_cont_1 = *lt->bpl_; + long long_cont_2 = lt->bpl_[3]; + + ACE_ASSERT (long_cont_1 == 1000); + ACE_ASSERT (long_cont_2 == 1003); + + ACE_ALLOCATOR_RETURN (ptr, + allocator->malloc (sizeof (Long_Test)), + 0); + lt = new (ptr) Long_Test; + + lt->array_[0] = 2000; + lt->array_[1] = 2001; + lt->array_[2] = 2002; + lt->array_[3] = 2003; + lt->array_[4] = 2004; + lt->bpl_ = lt->array_; + + data2->long_test_= lt; + + long long_cont_3 = *lt->bpl_; + long long_cont_4 = lt->bpl_[4]; + + ACE_ASSERT (long_cont_3 == 2000); + ACE_ASSERT (long_cont_4 == 2004); + + return data2; +} + +static void +print (const char *process_name, + Test_Data *data) +{ + for (Test_Data *t = data; t != 0; t = t->next_) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<<<< (%P) %s\ni1_ = %d, i2_ = %d, i3_ = %d, d1_ = %f\n"), + process_name, + t->i1_, + t->i2_, + t->i3_, + t->d1_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("*t->bpl_ = %d, t->long_test_->array_[0] = ") + ACE_TEXT ("%d\n>>>>\n"), + *t->long_test_->bpl_, + t->long_test_->array_[0])); + } +} + +static int +parent (Test_Data *data) +{ + MALLOC *myalloc = myallocator (); + + { + ACE_GUARD_RETURN (ACE_Process_Mutex, guard, myalloc->mutex (), -1); + print ("parent", data); + } + + // Sleep for a 200 msecs so that the child will have a chance to spin! + ACE_OS::sleep (ACE_Time_Value (0, 200 * 1000)); + +#if defined (ACE_TEST_REMAP_ON_FAULT) + char *small_buf[1024]; + int cntr; + + for (cntr = 0 ; cntr < 1024; ++cntr) + small_buf[cntr] = (char *) myalloc->malloc (1); + char *big_buf = (char *) myalloc->malloc (1024 * 4069); +#endif /* ACE_TEST_REMAP_ON_FAULT */ + + int result = myalloc->bind ("bar", data); + +#if defined (ACE_TEST_REMAP_ON_FAULT) + myalloc->free (big_buf); + for (cntr = 0 ; cntr < 1024; ++cntr) + myalloc->free (small_buf[cntr]); +#endif /* ACE_TEST_REMAP_ON_FAULT */ + + ACE_ASSERT (result != -1); + return 0; +} + +static int +child (void) +{ + void *bar = 0; + // Perform "busy waiting" here until the parent stores data under a + // new name called "bar" in <ACE_Malloc>. This isn't a good design + // -- it's just to test that synchronization is working across + // processes via <ACE_Malloc>. + for (ACE_Time_Value timeout (0, 1000 * 10); + myallocator ()->find ("bar", + bar) == -1; + ) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) sleeping for 10 milliseconds!\n"))); + ACE_OS::sleep (timeout); + } + + print ("child", + reinterpret_cast<Test_Data *> (bar)); + return 0; +} + +#if defined (ACE_WIN32) +// On Win9x/Me, a shared address needs to be on the shared arena, +// betweeen the second and third megabyte in the virtual address space +// of the process. Also, a mapped view of a file is shared on the same +// virtual address on every 32 bit process. On WinNT/2k, memory above +// 2Gb is reserved for the system. So, we need to check at runtime +// (we want an ACE_HAS_WINNT4 == 0 ace to run on either). +// To catch any odd case arising from Pharlap and/or WinCE, do the +// run time check and run the NT4-or-better code unless we're on +// CE or something other than NT4 (Pharlap reports itself as NT 3.51). +static void +get_base_addrs (void) +{ + OSVERSIONINFO vinfo; + vinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (::GetVersionEx(&vinfo) == 0) + return; + + if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && + vinfo.dwMajorVersion >= 4) + PARENT_BASE_ADDR = (char*) (64 * 1024*1024); + else + PARENT_BASE_ADDR = (char*) ((2048UL + 512UL)*(1024UL*1024UL)); + + CHILD_BASE_ADDR = CHILD_ADDR_DELTA + (char*) PARENT_BASE_ADDR; +} +#endif /* defined (ACE_WIN32) */ + +int +run_main (int argc, ACE_TCHAR *[]) +{ +#if defined (ACE_WIN32) + get_base_addrs(); +#endif + + if (argc == 1) + { + ACE_START_TEST (ACE_TEXT ("Malloc_Test")); + ACE_INIT_LOG (ACE_TEXT ("Malloc_Test-child")); + + init_test (PARENT_BASE_ADDR); + + ACE_Control_Block::print_alignment_info (); +# if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) + ACE_PI_Control_Block::print_alignment_info (); +# endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1 */ + + // No arguments means we're the parent process. + ACE_Process_Options options (1); + + options.command_line (EXE_LOCATION + ACE_TEXT ("Malloc_Test") + ACE_PLATFORM_EXE_SUFFIX + ACE_TEXT (" run_as_test")); + +#ifdef ACE_HAS_WINCE + // \Windows\Start Menu is where Malloc_Test.exe will be downloaded to. + // Check project setting for the directory information if needs to be changed. + options.process_name(ACE_TEXT("\\Windows\\Start Menu\\Malloc_Test.exe")); +#endif + + MALLOC *myalloc = myallocator (PARENT_BASE_ADDR); + + Test_Data *data = initialize (myalloc); + ACE_ASSERT (data != 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) PARENT allocator at = %@, ") + ACE_TEXT ("data allocated at %@\n"), + myalloc, + data)); + myalloc->dump (); + int result = myalloc->bind ("foo", data); + ACE_ASSERT (result != -1); + + ACE_Process p; + pid_t pid = p.spawn (options); + if (pid == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn")), 1); + + parent (data); + + // Synchronize on the exit of the child. + result = p.wait (); + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("wait")), 1); + ACE_ASSERT (myalloc->ref_counter () == 1); + myalloc->remove (); + ACE_END_TEST; + return 0; + } + else + { + // In this case we're the child process. + ACE_APPEND_LOG (ACE_TEXT ("Malloc_Test-child")); + + void *data = 0; + MALLOC *myalloc = myallocator (CHILD_BASE_ADDR); + int result = myalloc->find ("foo", data); + ACE_ASSERT (result != -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) CHILD allocator at = %@, ") + ACE_TEXT ("data allocated at %@\n"), + myalloc, + data)); + myalloc->dump (); + child (); + myalloc->release (); + ACE_END_LOG; + return 0; + } +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Malloc_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("process creation is not supported on this ") + ACE_TEXT ("platform\n"))); + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_PROCESS_SPAWN */ diff --git a/ACE/tests/Malloc_Test.h b/ACE/tests/Malloc_Test.h new file mode 100644 index 00000000000..8fda7368ee4 --- /dev/null +++ b/ACE/tests/Malloc_Test.h @@ -0,0 +1,44 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Malloc_Test.h +// +// = DESCRIPTION +// This class gets its own header file to work around AIX C++ +// compiler "features" related to template instantiation... It is +// only used by Malloc_Test.cpp. +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> + +// ============================================================================ + +#ifndef ACE_TESTS_MALLOC_TEST_H +#define ACE_TESTS_MALLOC_TEST_H + +#include "ace/Based_Pointer_T.h" + +// Some test data. +struct Long_Test +{ + ACE_Based_Pointer_Basic<long> bpl_; + long array_[10]; +}; + +// Some more test data. +struct Test_Data +{ + int i1_; + int i2_; + int i3_; + double d1_; + ACE_Based_Pointer<Test_Data> next_; + ACE_Based_Pointer<Long_Test> long_test_; +}; + +#endif /* ACE_TESTS_MALLOC_TEST_H */ diff --git a/ACE/tests/Manual_Event_Test.cpp b/ACE/tests/Manual_Event_Test.cpp new file mode 100644 index 00000000000..da3af8d1ef9 --- /dev/null +++ b/ACE/tests/Manual_Event_Test.cpp @@ -0,0 +1,217 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Manual_Event Test +// +// = DESCRIPTION +// This test verifies the functionality of the <ACE_Manual_Event> +// implementation. +// +// = AUTHOR +// Martin Corino <mcorino@remedy.nl> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Manual_Event.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Atomic_Op.h" + +ACE_RCSID(tests, Manual_Event_Test, "$Id$") + +// msec that times are allowed to differ before test fails. +#if defined (ACE_HAS_HI_RES_TIMER) || defined (ACE_HAS_AIX_HI_RES_TIMER) || \ + defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER) || \ + defined (ACE_HAS_POWERPC_TIMER) +# define ACE_ALLOWED_SLACK 100 +#else /* don't have a high-res timer */ +# define ACE_ALLOWED_SLACK 1100 +#endif /* don't have a high-res timer */ + +// Test results, 'success' is 0 +static int test_result = 0; + +#if defined (ACE_HAS_THREADS) + +// Event used in the tests. Start it "unsignalled" (i.e., its initial +// state is 0). +static ACE_Manual_Event evt ((unsigned int) 0); + +// Number of worker threads. +static long n_workers = 10; + +// Number of wakeups. +#if defined (ACE_HAS_BUILTIN_ATOMIC_OP) +static ACE_Atomic_Op<ACE_Thread_Mutex, long> n_awoken; +static ACE_Atomic_Op<ACE_Thread_Mutex, long> n_awoken2; +#else +static long n_awoken; +static long n_awoken2; +#endif + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-w n_workers] [-n iteration_count]\n"))); + ACE_OS::exit (1); +} + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("w:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'w': + n_workers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + print_usage_and_die (); + break; + } +} + +// Worker tries to acquire the semaphore, hold it for a while, and +// then manually releases it. + +static void * +worker (void *) +{ + if (evt.wait() == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Failed waiting for pulse()")), + 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) awake\n"))); + + if (++n_awoken < n_workers) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_Time_Value wait (1, 0); // Wait 10 sec + //FUZZ: enable check_for_lack_ACE_OS + + ACE_Time_Value tv = ACE_OS::gettimeofday () + wait; + + if (evt.wait (&tv) == -1) + { + // verify that we have ETIME + if (ACE_OS::last_error() == ETIME) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) timeout\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) wait failed %p\n"), + ACE_TEXT ("but not with ETIME"))); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) awake in time\n"))); + + if (++n_awoken2 >= (n_workers/2)) + evt.reset(); // reset signal (rest times out) + } + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) last awake; send signal\n"))); + // last one wakes others + if (evt.signal() == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("signal"))); + + ACE_OS::sleep (ACE_Time_Value (0, 200 * 1000 * 100)); // 200 msec + } + + if (evt.wait() == -1) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Failed waiting for signal()\n"))); + //FUZZ: enable check_for_lack_ACE_OS + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) worker finished\n"))); + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Test event functionality. + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Manual_Event_Test")); + +#if defined (ACE_HAS_THREADS) + parse_args (argc, argv); + + if (ACE_Thread_Manager::instance ()->spawn_n + (static_cast<size_t> (n_workers), + ACE_THR_FUNC (worker), + 0, + THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), + 1); + + // gives all workers chance to start + ACE_OS::sleep (5); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("sending pulse()\n"))); + + // Release the all workers. + if (evt.pulse () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("pulse")), + 1); + + // Wait 2 sec + ACE_OS::sleep (2); + + //FUZZ: disable check_for_lack_ACE_OS + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("sending signal()\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + // Signal + if (evt.signal () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("signal")), + 1); + + ACE_Thread_Manager::instance ()->wait (); + +#else + + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return test_result; +} diff --git a/ACE/tests/Map_Manager_Test.cpp b/ACE/tests/Map_Manager_Test.cpp new file mode 100644 index 00000000000..c220e285a06 --- /dev/null +++ b/ACE/tests/Map_Manager_Test.cpp @@ -0,0 +1,956 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Map_Manager_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the <ACE_Map_Manager> and +// <ACE_Hash_Map_Manager> that illustrates how to use the forward +// and reverse iterators. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu>, +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, and +// Kirthika Parameswaran <kirthika@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Map_Manager.h" +#include "ace/Hash_Map_Manager.h" +#include "ace/Active_Map_Manager.h" +#include "ace/Profile_Timer.h" +#include "ace/Hash_Cache_Map_Manager_T.h" +#include "ace/Caching_Strategies_T.h" +#include "ace/Pair_T.h" + +ACE_RCSID(tests, Map_Manager_Test, "$Id$") + +typedef ACE_Null_Mutex MUTEX; +typedef ACE_UINT32 TYPE; +typedef ACE_Active_Map_Manager_Key + ACTIVE_KEY; +typedef ACE_Hash<TYPE> + HASH_KEY; +typedef ACE_Equal_To<TYPE> + COMPARE_KEYS; + +typedef ACE_Map_Manager <TYPE, TYPE, MUTEX> + MAP_MANAGER; +typedef ACE_Map_Iterator <TYPE, TYPE, MUTEX> + ITERATOR; +typedef ACE_Map_Reverse_Iterator <TYPE, TYPE, MUTEX> + REVERSE_ITERATOR; +typedef ACE_Map_Entry <TYPE, TYPE> + MAP_ENTRY; +typedef ACE_Hash_Map_Manager_Ex <TYPE, TYPE, HASH_KEY, COMPARE_KEYS, MUTEX> + HASH_MAP_MANAGER; +typedef ACE_Hash_Map_Iterator_Ex <TYPE, TYPE, HASH_KEY, COMPARE_KEYS, MUTEX> + HASH_ITERATOR; +typedef ACE_Hash_Map_Reverse_Iterator_Ex <TYPE, TYPE, HASH_KEY, COMPARE_KEYS, MUTEX> + HASH_REVERSE_ITERATOR; +typedef ACE_Hash_Map_Entry <TYPE, TYPE> + HASH_ENTRY; +typedef ACE_Active_Map_Manager <TYPE> + ACTIVE_MAP_MANAGER; + +typedef ACE_Hash_Map_Manager_Ex<TYPE, ACE_Pair<TYPE, int>, HASH_KEY, COMPARE_KEYS, MUTEX> + CACHE_MAP_IMPL; +typedef ACE_Hash_Map_Iterator_Ex<TYPE, ACE_Pair<TYPE, int>, HASH_KEY, COMPARE_KEYS, MUTEX> + CACHE_ITER_IMPL; +typedef ACE_Hash_Map_Reverse_Iterator_Ex<TYPE, ACE_Pair<TYPE, int>, HASH_KEY, COMPARE_KEYS, MUTEX> + CACHE_REV_ITER_IMPL; +typedef int ATTR; +typedef ACE_Null_Cleanup_Strategy<TYPE, TYPE, CACHE_MAP_IMPL> + NULL_CLEANUP; +typedef ACE_Null_Caching_Utility <TYPE, TYPE, CACHE_MAP_IMPL, CACHE_ITER_IMPL, ATTR> + NULL_UTILITY; +typedef ACE_Null_Caching_Strategy<ATTR, NULL_UTILITY> + NULL_CACHING_STRATEGY; +typedef ACE_Cache_Map_Manager<TYPE, TYPE, CACHE_MAP_IMPL, CACHE_ITER_IMPL, CACHE_REV_ITER_IMPL, NULL_CACHING_STRATEGY, ATTR> + CACHE_MAP_MANAGER; +typedef ACE_Hash_Cache_Map_Manager<TYPE, TYPE, HASH_KEY, COMPARE_KEYS, NULL_CACHING_STRATEGY, ATTR> + HASH_CACHE_MAP_MANAGER; + +static void +test_cache_map_manager (size_t table_size, + size_t iterations, + int test_iterators) +{ + NULL_CACHING_STRATEGY null_caching_strategy; + CACHE_MAP_MANAGER map (null_caching_strategy, + table_size); + TYPE i; + TYPE j = TYPE (); + ssize_t k; + + for (i = 0; i < iterations; i++) + ACE_ASSERT (map.bind (i, i) != -1); + + if (test_iterators) + { + { + i = 0; + + CACHE_MAP_MANAGER::ITERATOR end = map.end (); + + for (CACHE_MAP_MANAGER::ITERATOR iter = map.begin (); + iter != end; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + (*iter).first (), + (*iter).second ())); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + + { + k = iterations - 1; + + CACHE_MAP_MANAGER::REVERSE_ITERATOR rend = map.rend (); + + for (CACHE_MAP_MANAGER::REVERSE_ITERATOR iter = map.rbegin (); + iter != rend; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + (*iter).first (), + (*iter).second ())); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + + { + i = 0; + + CACHE_MAP_MANAGER::iterator end = map.end (); + + for (CACHE_MAP_MANAGER::iterator iter = map.begin (); + iter != end; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + (*iter).first (), + (*iter).second ())); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + + { + k = iterations - 1; + + CACHE_MAP_MANAGER::reverse_iterator rend = map.rend (); + + for (CACHE_MAP_MANAGER::reverse_iterator iter = map.rbegin (); + iter != rend; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + (*iter).first (), + (*iter).second ())); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + + } + + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.find (i, j) != -1); + ACE_ASSERT (i == j); + } + + size_t remaining_entries = iterations; + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.unbind (i) != -1); + --remaining_entries; + ACE_ASSERT (map.current_size () == remaining_entries); + } + +} + +static void +test_hash_cache_map_manager (size_t table_size, + size_t iterations, + int test_iterators) +{ + NULL_CACHING_STRATEGY null_caching_strategy; + HASH_CACHE_MAP_MANAGER map (null_caching_strategy, + table_size); + TYPE i; + TYPE j = 0; + ssize_t k; + + for (i = 0; i < iterations; i++) + ACE_ASSERT (map.bind (i, i) != -1); + + if (test_iterators) + { + { + i = 0; + + HASH_CACHE_MAP_MANAGER::ITERATOR end = map.end (); + + for (HASH_CACHE_MAP_MANAGER::ITERATOR iter = map.begin (); + iter != end; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + (*iter).first (), + (*iter).second ())); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + + { + k = iterations - 1; + + HASH_CACHE_MAP_MANAGER::REVERSE_ITERATOR rend = map.rend (); + + for (HASH_CACHE_MAP_MANAGER::REVERSE_ITERATOR iter = map.rbegin (); + iter != rend; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + (*iter).first (), + (*iter).second ())); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + + { + i = 0; + + HASH_CACHE_MAP_MANAGER::iterator end = map.end (); + + for (HASH_CACHE_MAP_MANAGER::iterator iter = map.begin (); + iter != end; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + (*iter).first (), + (*iter).second ())); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + + { + k = iterations - 1; + + HASH_CACHE_MAP_MANAGER::reverse_iterator rend = map.rend (); + + for (HASH_CACHE_MAP_MANAGER::reverse_iterator iter = map.rbegin (); + iter != rend; + ++iter) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + (*iter).first (), + (*iter).second ())); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + + } + + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.find (i, j) != -1); + ACE_ASSERT (i == j); + } + + size_t remaining_entries = iterations; + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.unbind (i) != -1); + --remaining_entries; + ACE_ASSERT (map.current_size () == remaining_entries); + } +} + + +static void +test_active_map_manager (size_t table_size, + size_t iterations, + int test_iterators) +{ + ACTIVE_MAP_MANAGER map (table_size); + TYPE i; + TYPE j = 0; + ssize_t k; + + ACTIVE_MAP_MANAGER::key_type *active_keys; + + ACE_NEW (active_keys, + ACTIVE_MAP_MANAGER::key_type[iterations]); + + for (i = 0; + i < iterations; + i++) + ACE_ASSERT (map.bind (i, active_keys[i]) != -1); + + if (test_iterators) + { + { + i = 0; + + ACTIVE_MAP_MANAGER::iterator end = map.end (); + + for (ACTIVE_MAP_MANAGER::iterator iter = map.begin (); + iter != end; + ++iter) + { + ACTIVE_MAP_MANAGER::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d-%d|%d)"), + i, + entry.ext_id_.slot_index (), + entry.ext_id_.slot_generation (), + entry.int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + k = iterations - 1; + + ACTIVE_MAP_MANAGER::reverse_iterator rend = map.rend (); + + for (ACTIVE_MAP_MANAGER::reverse_iterator iter = map.rbegin (); + iter != rend; + ++iter) + { + ACTIVE_MAP_MANAGER::ENTRY &entry = *iter; + ACE_UNUSED_ARG (entry); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d-%d|%d)"), + k, + entry.ext_id_.slot_index (), + entry.ext_id_.slot_generation (), + entry.int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + } + + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.find (active_keys[i], j) != -1); + ACE_ASSERT (i == j); + } + + size_t remaining_entries = iterations; + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.unbind (active_keys[i]) != -1); + --remaining_entries; + ACE_ASSERT (map.current_size () == remaining_entries); + } + + delete [] active_keys; +} + +static void +test_hash_map_manager (size_t table_size, + size_t iterations, + int test_iterators) +{ + HASH_MAP_MANAGER map (table_size); + TYPE i; + TYPE j = 0; + ssize_t k; + + for (i = 0; i < iterations; i++) + ACE_ASSERT (map.bind (i, i) != -1); + + if (test_iterators) + { + { + i = 0; + + HASH_ITERATOR end = map.end (); + for (HASH_ITERATOR iter = map.begin (); + iter != end; + ++iter) + { + HASH_ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry.ext_id_, + entry.int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + i = 0; + HASH_ENTRY *entry = 0; + + for (HASH_ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry->ext_id_, + entry->int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + k = iterations - 1; + HASH_REVERSE_ITERATOR rend = map.rend (); + + for (HASH_REVERSE_ITERATOR iter = map.rbegin (); + iter != rend; + ++iter) + { + HASH_ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry.ext_id_, + entry.int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + { + k = iterations - 1; + HASH_ENTRY *entry = 0; + + for (HASH_REVERSE_ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry->ext_id_, + entry->int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + { + i = 0; + + HASH_MAP_MANAGER::iterator end = map.end (); + for (HASH_MAP_MANAGER::iterator iter = map.begin (); + iter != end; + ++iter) + { + HASH_MAP_MANAGER::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry.ext_id_, + entry.int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + i = 0; + HASH_MAP_MANAGER::ENTRY *entry = 0; + + for (HASH_MAP_MANAGER::ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry->ext_id_, + entry->int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + k = iterations - 1; + HASH_MAP_MANAGER::reverse_iterator rend = map.rend (); + + for (HASH_MAP_MANAGER::reverse_iterator iter = map.rbegin (); + iter != rend; + ++iter) + { + HASH_MAP_MANAGER::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry.ext_id_, + entry.int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + { + k = iterations - 1; + HASH_MAP_MANAGER::ENTRY *entry = 0; + + for (HASH_MAP_MANAGER::REVERSE_ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry->ext_id_, + entry->int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + } + + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.find (i, j) != -1); + ACE_ASSERT (i == j); + } + + size_t remaining_entries = iterations; + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.unbind (i) != -1); + --remaining_entries; + ACE_ASSERT (map.current_size () == remaining_entries); + } +} + +static void +test_map_manager (size_t table_size, + size_t iterations, + int test_iterators) +{ + MAP_MANAGER map (table_size); + TYPE i; + TYPE j = 0; + ssize_t k; + + for (i = 0; i < iterations; ++i) + ACE_ASSERT (map.bind (i, i) != -1); + + if (test_iterators) + { + { + i = 0; + + ITERATOR end = map.end (); + for (ITERATOR iter = map.begin (); + iter != end; + ++iter) + { + MAP_ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry.ext_id_, + entry.int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + i = 0; + MAP_ENTRY *entry = 0; + + for (ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry->ext_id_, + entry->int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + k = iterations - 1; + REVERSE_ITERATOR rend = map.rend (); + + for (REVERSE_ITERATOR iter = map.rbegin (); + iter != rend; + ++iter) + { + MAP_ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry.ext_id_, + entry.int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + { + k = iterations - 1; + MAP_ENTRY *entry = 0; + + for (REVERSE_ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry->ext_id_, + entry->int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + { + i = 0; + + MAP_MANAGER::iterator end = map.end (); + for (MAP_MANAGER::iterator iter = map.begin (); + iter != end; + ++iter) + { + MAP_MANAGER::ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry.ext_id_, + entry.int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + i = 0; + MAP_MANAGER::ENTRY *entry = 0; + + for (MAP_MANAGER::ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + i, + entry->ext_id_, + entry->int_id_)); + ++i; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (i == iterations); + } + + { + k = iterations - 1; + MAP_MANAGER::reverse_iterator rend = map.rend (); + + for (MAP_MANAGER::reverse_iterator iter = map.rbegin (); + iter != rend; + ++iter) + { + MAP_ENTRY &entry = *iter; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry.ext_id_, + entry.int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + + { + k = iterations - 1; + MAP_MANAGER::ENTRY *entry = 0; + + for (MAP_MANAGER::REVERSE_ITERATOR iterator (map); + iterator.next (entry) != 0; + iterator.advance ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + k, + entry->ext_id_, + entry->int_id_)); + k--; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_ASSERT (k == -1); + } + } + + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.find (i, j) != -1); + ACE_ASSERT (i == j); + } + + size_t remaining_entries = iterations; + for (i = 0; i < iterations; ++i) + { + ACE_ASSERT (map.unbind (i) != -1); + --remaining_entries; + ACE_ASSERT (map.current_size () == remaining_entries); + } + + // + // This is extra for the map manager. + // + for (i = 0; i < iterations; ++i) + ACE_ASSERT (map.bind (i, i) != -1); + + // Unbind in one swoop. + map.unbind_all (); + ACE_ASSERT (map.current_size () == 0); + + for (i = 0; i < iterations; ++i) + ACE_ASSERT (map.bind (i, i) != -1); + + // Unbind one at a time. + MAP_MANAGER::iterator end = map.end (); + while (1) + { + MAP_MANAGER::iterator iter = map.begin (); + if (iter == end) + break; + ACE_ASSERT (map.unbind ((*iter).ext_id_) != -1); + } + + ACE_ASSERT (map.current_size () == 0); +} + +static void +run_test (void (*ptf) (size_t, size_t, int), + size_t table_size, + size_t iterations, + int test_iterators, + const ACE_TCHAR *test_name) +{ + ACE_Profile_Timer timer; + timer.start (); + + (*ptf) (table_size, iterations, test_iterators); + + timer.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + + timer.elapsed_time (et); + + ACE_TCHAR *test_iterators_string = 0; + + if (test_iterators) + test_iterators_string = + const_cast<ACE_TCHAR*> (ACE_TEXT ( "includes executing iterators")); + else + test_iterators_string = + const_cast<ACE_TCHAR*> (ACE_TEXT ("doesn't include executing iterators")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to test a map of size %d for %d iterations using %s (%s)\n"), + table_size, + iterations, + test_name, + test_iterators_string)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, + et.user_time, + et.system_time)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.real_time / ACE_timer_t (iterations)) * 1000000)); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Map_Manager_Test")); + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + size_t table_size = ACE_MAX_ITERATIONS / 2; + size_t iterations = ACE_MAX_ITERATIONS; + int test_iterators = 1; + + if (argc > 1) + table_size = ACE_OS::atoi (argv[1]); + + if (argc > 2) + iterations = ACE_OS::atoi (argv[2]); + + if (argc > 3) + test_iterators = ACE_OS::atoi (argv[3]); + + // Test the <ACE_Map_Manager>. + run_test (&test_map_manager, + table_size, + iterations, + test_iterators, + ACE_TEXT ("Map_Manager")); + + // Test the <ACE_Hash_Map_Manager>. + run_test (&test_hash_map_manager, + table_size, + iterations, + test_iterators, + ACE_TEXT ("Hash_Map_Manager")); + + // Test the <ACE_Hash_Map_Manager>. + run_test (&test_active_map_manager, + table_size, + iterations, + test_iterators, + ACE_TEXT ("Active_Map_Manager")); + + // Test the <ACE_Cache_Map_Manager>. + run_test (&test_cache_map_manager, + table_size, + iterations, + test_iterators, + ACE_TEXT ("Cache_Map_Manager")); + + // Test the <ACE_Hash_Cache_Map_Manager>. + run_test (&test_hash_cache_map_manager, + table_size, + iterations, + test_iterators, + ACE_TEXT ("Hash_Cache_Map_Manager")); + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Map_Test.cpp b/ACE/tests/Map_Test.cpp new file mode 100644 index 00000000000..fb08b5ae034 --- /dev/null +++ b/ACE/tests/Map_Test.cpp @@ -0,0 +1,375 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Map_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the <ACE_Map> and illustrates how to +// use the forward and reverse iterators. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "Map_Test.h" +#include "ace/Map_T.h" +#include "ace/Profile_Timer.h" + +ACE_RCSID(tests, Map_Test, "$Id$") + +#undef THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL +#define THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL(X) \ + ((X) \ + ? static_cast<void>(0) \ + : ACE_VERSIONED_NAMESPACE_NAME::__ace_assert(__FILE__, __LINE__, ACE_TEXT_CHAR_TO_TCHAR (#X))) + +// Value type. +typedef size_t VALUE; + +// Generic map type. +typedef ACE_Map<KEY, VALUE> TEST_MAP; + +// Manager Manager adapter. +typedef ACE_Map_Manager_Adapter<KEY, VALUE, Key_Generator> MAP_MANAGER_ADAPTER; + +// Hash Manager Manager adapter. +typedef ACE_Hash_Map_Manager_Ex_Adapter<KEY, VALUE, Hash_Key, ACE_Equal_To<KEY>, Key_Generator> HASH_MAP_MANAGER_ADAPTER; + +// Active Manager Manager adapter. +typedef ACE_Active_Map_Manager_Adapter<KEY, VALUE, Key_Adapter> ACTIVE_MAP_MANAGER_ADAPTER; + +static void +functionality_test (TEST_MAP &map, + size_t iterations) +{ + size_t counter; + VALUE i; + KEY *original_keys = new KEY[iterations]; + KEY *modified_keys = new KEY[iterations]; + + // Setup the keys to have some initial data. + for (i = 0; + i < iterations; + ++i) + { + original_keys[i].size (sizeof i / sizeof (KEY::TYPE)); + ACE_OS::memcpy (&original_keys[i][0], + &i, + sizeof i); + } + + // Make a copy of the keys so that we can compare with the original + // keys later. + for (i = 0; i < iterations; ++i) + { + modified_keys[i] = original_keys[i]; + } + + // Add to the map, allowing keys to be modified. + counter = 0; + for (i = 0; i < iterations; ++i) + { + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.bind_modify_key (i, modified_keys[i]) == 0); + ++counter; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.current_size () == counter); + } + + // Forward iteration... + { + counter = 0; + TEST_MAP::iterator end = map.end (); + + for (TEST_MAP::iterator iter = map.begin (); + iter != end; + ++iter, ++counter) + { + TEST_MAP::value_type entry = *iter; + + // Recover original key. + KEY original_key; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.recover_key (entry.first (), + original_key) == 0); + + // Make sure recovering keys work. + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (original_keys[entry.second ()] == original_key); + + // Obtain value from key. + VALUE original_value; + ACE_OS::memcpy (&original_value, + &original_key[0], + sizeof original_value); + + // Debugging info. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + counter, + original_value, + entry.second ())); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (counter == iterations); + } + + // Reverse iteration... + { + counter = iterations; + TEST_MAP::reverse_iterator end = map.rend (); + + for (TEST_MAP::reverse_iterator iter = map.rbegin (); + iter != end; + ++iter) + { + --counter; + TEST_MAP::value_type entry = *iter; + + // Recover original key. + KEY original_key; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.recover_key (entry.first (), + original_key) == 0); + + // Make sure recovering keys work. + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (original_keys[entry.second ()] == original_key); + + // Obtain value from key. + VALUE original_value; + ACE_OS::memcpy (&original_value, + &original_key[0], + sizeof original_value); + + // Debugging info. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d|%d|%d)"), + counter, + original_value, + entry.second ())); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (counter == 0); + } + + // Search using the modified keys. + for (i = 0; i < iterations; ++i) + { + VALUE j; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.find (modified_keys[i], j) != -1); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (i == j); + } + + // Rmoved keys from map. + counter = iterations; + for (i = 0; i < iterations; ++i) + { + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.unbind (modified_keys[i]) != -1); + --counter; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.current_size () == counter); + } + + // Cleanup. + delete[] modified_keys; + delete[] original_keys; +} + +static void +insert_test (TEST_MAP &map, + size_t iterations, + KEY *keys) +{ + // Add to the map, allowing keys to be created by the map. + size_t counter = 0; + for (VALUE i = 0; i < iterations; ++i) + { + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.bind_create_key (i, keys[i]) == 0); + ++counter; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.current_size () == counter); + } +} + +static void +find_test (TEST_MAP &map, + size_t iterations, + KEY *keys) +{ + // Find system generated keys. + for (VALUE i = 0; i < iterations; ++i) + { + VALUE j; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.find (keys[i], j) != -1); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (i == j); + } +} + +static void +unbind_test (TEST_MAP &map, + size_t iterations, + KEY *keys) +{ + // Remove system generated keys. + size_t counter = iterations; + for (VALUE i = 0; i < iterations; ++i) + { + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.unbind (keys[i]) != -1); + --counter; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (map.current_size () == counter); + } +} + +static void +performance_test (void (*ptf) (TEST_MAP &, size_t, KEY *), + TEST_MAP &map, + size_t iterations, + KEY *keys, + size_t table_size, + const ACE_TCHAR *test_name) +{ + ACE_Profile_Timer timer; + timer.start (); + + (*ptf) (map, iterations, keys); + + timer.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to run %s of size %d for %d iterations\n"), + test_name, + table_size, + iterations)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, + et.user_time, + et.system_time)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.real_time / ACE_timer_t (iterations)) * 1000000)); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Map_Test")); + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + size_t table_size = ACE_MAX_ITERATIONS / 2; + size_t iterations = ACE_MAX_ITERATIONS; + size_t functionality_tests = 1; + + if (argc > 1) + functionality_tests = ACE_OS::atoi (argv[1]); + + if (argc > 2) + table_size = ACE_OS::atoi (argv[2]); + + if (argc > 3) + iterations = ACE_OS::atoi (argv[3]); + + MAP_MANAGER_ADAPTER map1 (table_size); + HASH_MAP_MANAGER_ADAPTER map2 (table_size); + ACTIVE_MAP_MANAGER_ADAPTER map3 (table_size); + + if (functionality_tests) + { + // Functionality test of the maps. + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nMap Manager functionality test\n"))); + functionality_test (map1, iterations); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nHash Map Manager functionality test\n"))); + functionality_test (map2, iterations); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nActive Map Manager functionality test\n"))); + functionality_test (map3, iterations); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + } + + // Performance test of the maps. + KEY *keys = new KEY[iterations]; + + // Map Manager + performance_test (&insert_test, + map1, + iterations, + keys, + table_size, + ACE_TEXT ("Map Manager (insert test)")); + performance_test (&find_test, + map1, + iterations, + keys, + table_size, + ACE_TEXT ("Map Manager (find test)")); + performance_test (&unbind_test, + map1, + iterations, + keys, + table_size, + ACE_TEXT ("Map Manager (unbind test)")); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + + // Hash Map Manager + performance_test (&insert_test, + map2, + iterations, + keys, + table_size, + ACE_TEXT ("Hash Map Manager (insert test)")); + performance_test (&find_test, + map2, + iterations, + keys, + table_size, + ACE_TEXT ("Hash Map Manager (find test)")); + performance_test (&unbind_test, + map2, + iterations, + keys, + table_size, + ACE_TEXT ("Hash Map Manager (unbind test)")); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + + // Active Map Manager + performance_test (&insert_test, + map3, + iterations, + keys, + table_size, + ACE_TEXT ("Active Map Manager (insert test)")); + performance_test (&find_test, + map3, + iterations, + keys, + table_size, + ACE_TEXT ("Active Map Manager (find test)")); + performance_test (&unbind_test, + map3, + iterations, + keys, + table_size, + ACE_TEXT ("Active Map Manager (unbind test)")); + + delete[] keys; + + ACE_LOG_MSG->set_flags (ACE_Log_Msg::VERBOSE_LITE); + ACE_END_TEST; + + return 0; +} +#undef THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL diff --git a/ACE/tests/Map_Test.h b/ACE/tests/Map_Test.h new file mode 100644 index 00000000000..c881a3f7645 --- /dev/null +++ b/ACE/tests/Map_Test.h @@ -0,0 +1,151 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Map_Test.h +// +// = DESCRIPTION +// This file has the class definitions needed for template generation in +// Map_Test.cpp. They have to be in a separate file so AIX xlC can +// find them at auto-instantiate time. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_MAP_TEST_H +#define ACE_TESTS_MAP_TEST_H + +#include "ace/OS_NS_string.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Active_Map_Manager.h" +#include "ace/Containers.h" + +// Key data type. +typedef ACE_Array<char> KEY; + +class Key_Generator +{ + // = TITLE + // Defines a key generator. + // + // = DESCRIPTION + // This class is used in adapters of maps that do not produce keys. +public: + + Key_Generator (void) + : counter_ (0) + { + } + + int operator() (KEY &key) + { + // Keep original information in the key intact. + size_t original_size = key.size (); + + // Size of this counter key. + size_t counter_key_size = sizeof this->counter_; + + // Resize to accommodate both the original data and the new key. + key.size (counter_key_size + original_size); + + // Add new key data. + ACE_OS::memcpy (&key[original_size], + &++this->counter_, + sizeof this->counter_); + + // Success. + return 0; + } + +private: + u_long counter_; +}; + +class Hash_Key +{ +public: + u_long operator () (const KEY &key) const + { + // Recover system generated part of key. + u_long value; + size_t counter_key_size = sizeof (u_long); + + // Copy system key from user key. + ACE_OS::memcpy (&value, + &key[key.size () - counter_key_size], + sizeof value); + + // Return the system key value as the hash value. + return value; + } +}; + +class Key_Adapter +{ +public: + + int encode (const KEY &original_key, + const ACE_Active_Map_Manager_Key &active_key, + KEY &modified_key) + { + // Keep original information in the key intact. + modified_key = original_key; + size_t original_size = modified_key.size (); + + // Size of active key. + size_t active_key_size = active_key.size (); + + // Resize to accommodate both the original data and the new active key. + modified_key.size (active_key_size + original_size); + + // Copy active key data into user key. + active_key.encode (&modified_key[original_size]); + + // Success. + return 0; + } + + int decode (const KEY &modified_key, + ACE_Active_Map_Manager_Key &active_key) + { + // Read the active key data from the back of the key. + size_t active_key_size = active_key.size (); + size_t original_size = modified_key.size () - active_key_size; + + // Read off value of index and generation. + active_key.decode (&modified_key[original_size]); + + // Success. + return 0; + } + + int decode (const KEY &modified_key, + KEY &original_key) + { + // Read the original user key data from the front of the + // modified key. + size_t active_key_size = ACE_Active_Map_Manager_Key::size (); + + // Copy all the data. + original_key = modified_key; + + // Resize to ignore active key data. + original_key.size (original_key.size () - active_key_size); + + // Success. + return 0; + } +}; + +#endif /* ACE_TESTS_MAP_TEST_H */ diff --git a/ACE/tests/Max_Default_Port_Test.cpp b/ACE/tests/Max_Default_Port_Test.cpp new file mode 100644 index 00000000000..8834a8b6254 --- /dev/null +++ b/ACE/tests/Max_Default_Port_Test.cpp @@ -0,0 +1,300 @@ +// $Id$ +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Max_Default_Port_Test.cpp +// +// = DESCRIPTION +// This is a test for ACE_MAX_DEFAULT_PORT value. The test tests the +// highest value of the port number at which an event handler can be +// registered and a Connector can be connected to. +// +// Some weird behaviour has been reported on Windows NT (sp 3) when +// the port number exceeds 65279 resulting ACE_MAX_DEFAULT_PORT to set +// to zero on that platform. +// +// In this test, the event handler is started at the port value +// USHRT_MAX and decremented for 'ports_to_test' port values and tested +// if the highest port number used agrees with ACE_MAX_DEFAULT_PORT value. +// +// +// = AUTHOR +// Chanaka Liyanaarachchi <chanaka@ociweb.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Thread_Manager.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Time_Value.h" + +#include "Max_Default_Port_Test.h" + +// implement a retry and recuperation mechanism for VxWorks because test will otherwise fail +// on a non-optimized kernel with several ETIME errors on the client connects. +#if defined (ACE_VXWORKS) +int retry_port_ = 0; +#endif + +My_Accept_Handler::My_Accept_Handler (ACE_INET_Addr &addr) + : addr_ (addr) +{ + if (addr.get_port_number() != 0) + this->open (addr); +} + + +My_Accept_Handler::~My_Accept_Handler () +{ + this->peer_acceptor_.close (); // Prevent handle leaks +} + + +int +My_Accept_Handler::open (ACE_INET_Addr &addr) +{ + + if (this->peer_acceptor_.open (addr, 1) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("My_Accept_Handler open"))); + ACE_OS::exit (1); + } + + return 0; + +} + +ACE_HANDLE +My_Accept_Handler::get_handle () const +{ + return this->peer_acceptor_.get_handle (); +} + +int +My_Accept_Handler::handle_input (ACE_HANDLE) +{ + + if (this->peer_acceptor_.accept(this->stream_, 0) == -1) { + ACE_ERROR((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("peer_acceptor.accept"))); + ACE_OS::exit(1); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("My_Accept_Handler::handle_input \n"))); + + // Close the opened stream, else it'll leak a handle. Don't close + // the acceptor here, though, because get_handle() needs it to + // correctly allow removal from the reactor later. It gets closed + // in the destructor. + this->stream_.close (); + + return 0; +} + +u_short +My_Accept_Handler::port () +{ + return this->addr_.get_port_number(); +} + +long max_connected_port = 0; + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = reinterpret_cast<ACE_INET_Addr *> (arg); + + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_LOCALHOST, + AF_INET); + + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Connecting to port %d\n"), + server_addr.get_port_number())); + + // Initiate connection with server; don't wait forever + if (con.connect (cli_stream, + server_addr, + &timeout) == -1) + { +#if defined (ACE_VXWORKS) + if (errno == ETIME) + { + if ( ++retry_port_<6 ) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Going to retry port %d\n"), + server_addr.get_port_number())); + } + } + if ( retry_port_>5 ) + { + retry_port_ = 0; +#endif + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + +#if defined (ACE_VXWORKS) + } +#endif + return 0; + } + +#if defined (ACE_VXWORKS) + retry_port_ = 0; +#endif + + // if connect succesful, what is the max port number we connected + // up to now. + int connected_port = server_addr.get_port_number (); + + if (connected_port > max_connected_port) + max_connected_port = connected_port; + + cli_stream.close (); + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + + ACE_START_TEST (ACE_TEXT ("Max_Default_Port_Test")); + + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + u_short max_listened_port = 0; + +#if defined (__Lynx__) + // LynxOS can handle only 80 test iterations. + // This needs to be investigated further -- olli 12.11.2007 + const u_short ports_to_test = 80; +#else + const u_short ports_to_test = 300; +#endif + + //Ports beyond 65279 were said to bad on NT sp 3. + for (u_short idx = USHRT_MAX; idx != USHRT_MAX - ports_to_test; --idx) + { +#if defined (ACE_VXWORKS) + if (retry_port_>0) + { + ++idx; + ACE_OS::sleep (ACE_Time_Value (2*ACE_DEFAULT_TIMEOUT)); + } +#endif + + ACE_INET_Addr addr (idx); + + My_Accept_Handler *eh = new My_Accept_Handler (addr); + + + if ( ACE_Reactor::instance()->register_handler ( + eh, + ACE_Event_Handler::ACCEPT_MASK) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Failed to register event handler")), + 1); + } + + ACE_DEBUG ((LM_DEBUG, "Registered event handler at %d\n", idx)); + + ACE_Time_Value tv (1); + +#if defined (ACE_HAS_THREADS) + + if (ACE_Thread_Manager::instance ()->spawn_n + (1, + ACE_THR_FUNC (client), + reinterpret_cast <void *> (&addr), + THR_NEW_LWP | THR_DETACHED) == -1) + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("thread create failed")), + 1); + + ACE_Thread_Manager::instance ()->wait (); + +#else + ACE_UNUSED_ARG (client); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) only one thread may be run in a process on this platform\n%a"), + 1)); +#endif //ACE_HAS_THREADS + + if (ACE_Reactor::instance()->handle_events (tv) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Reactor::handle_events")), + 1); + } + + // see if I can register a reactor at this port. + if (eh->port () == idx) + { + if (idx > max_listened_port) + max_listened_port = idx; + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Test Fail, listening port %d\n"), + eh->port()), + 1); + } + + ACE_Reactor::instance()->remove_handler ( + eh, + ACE_Event_Handler::ACCEPT_MASK | + ACE_Event_Handler::DONT_CALL); + delete eh; + } + + ACE_DEBUG ((LM_DEBUG, + "Value of ACE_MAX_DEFAULT_PORT %d\n", + ACE_MAX_DEFAULT_PORT)); + + ACE_DEBUG ((LM_DEBUG, + "Highest port value I can listen at %d\n", + max_listened_port)); + + ACE_DEBUG ((LM_DEBUG, + "Highest port value I can connect to %d\n", + max_connected_port)); + + if ((max_listened_port == ACE_MAX_DEFAULT_PORT) && + (max_connected_port == ACE_MAX_DEFAULT_PORT)) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Valid ACE_MAX_DEFAULT_PORT value: %d\n"), + max_listened_port)); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Invalid ACE_MAX_DEFAULT_PORT ") + ACE_TEXT ("or %d port may be busy; got to %d\n"), + ACE_MAX_DEFAULT_PORT, max_listened_port)); + + } + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Max_Default_Port_Test.h b/ACE/tests/Max_Default_Port_Test.h new file mode 100644 index 00000000000..78969cf05e1 --- /dev/null +++ b/ACE/tests/Max_Default_Port_Test.h @@ -0,0 +1,53 @@ +// $Id$ +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Max_Default_Port_Test.h +// +// = DESCRIPTION +// This is a test to verify ACE_MAX_DEFAULT_PORT is correct. +// +// = AUTHOR +// Chanaka Liyanaarachchi <chanaka@ociweb.com> +// +// =========================================================================== + + +#ifndef ACE_TESTS_MAX_DEFAULT_PORT_TEST +#define ACE_TESTS_MAX_DEFAULT_PORT_TEST + +#include "ace/Event_Handler.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/INET_Addr.h" + +class My_Accept_Handler : public ACE_Event_Handler +{ + // = Title + // A simple event handler + +public: + My_Accept_Handler (ACE_INET_Addr &addr); + ~My_Accept_Handler (); + + int open (ACE_INET_Addr &addr); + + ACE_HANDLE get_handle () const; + + int handle_input (ACE_HANDLE handle); + + u_short port (); + // Returns the port the event handler is listening at. + +private: + ACE_SOCK_Acceptor peer_acceptor_; + + ACE_SOCK_Stream stream_; + + ACE_INET_Addr addr_; +}; + +#endif /* ACE_TESTS_MAX_DEFAULT_PORT_TEST */ diff --git a/ACE/tests/Max_Default_Port_Test_IPV6.cpp b/ACE/tests/Max_Default_Port_Test_IPV6.cpp new file mode 100644 index 00000000000..bc0e9809ee0 --- /dev/null +++ b/ACE/tests/Max_Default_Port_Test_IPV6.cpp @@ -0,0 +1,255 @@ +// $Id$ +// ============================================================================ +/** + * @file Max_Default_Port_Test_IPV6.cpp + * + * @brief This is a test for ACE_MAX_DEFAULT_PORT value. + * + * The test tests the highest value of the port number at which an + * event handler can be registered and a Connector can be connected + * to. + * + * Some weird behaviour has been reported on Windows NT (sp 3) when + * the port number exceeds 65279 resulting ACE_MAX_DEFAULT_PORT to set + * to zero on that platform. + * + * In this test, the event handler is started at the port value + * USHRT_MAX and decremented for 300 port values and tested if the + * highest port number used agrees with ACE_MAX_DEFAULT_PORT value. + * + * @author Chanaka Liyanaarachchi <chanaka@ociweb.com> + * Brian Buesker <bbuesker@qualcomm.com> + */ +// ============================================================================ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Thread_Manager.h" + +#include "Max_Default_Port_Test.h" + +My_Accept_Handler::My_Accept_Handler (ACE_INET_Addr &addr) + : addr_ (addr) +{ + if (addr.get_port_number() != 0) + this->open (addr); +} + + +My_Accept_Handler::~My_Accept_Handler () +{ + this->peer_acceptor_.close (); // Prevent handle leaks +} + + +int +My_Accept_Handler::open (ACE_INET_Addr &addr) +{ + + if (this->peer_acceptor_.open (addr, 1) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("My_Accept_Handler open"))); + ACE_OS::exit (1); + } + + return 0; + +} + +ACE_HANDLE +My_Accept_Handler::get_handle () const +{ + return this->peer_acceptor_.get_handle (); +} + +int +My_Accept_Handler::handle_input (ACE_HANDLE) +{ + + if (this->peer_acceptor_.accept(this->stream_, 0) == -1) { + ACE_ERROR((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("peer_acceptor.accept"))); + ACE_OS::exit(1); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("My_Accept_Handler::handle_input \n"))); + + // Close the opened stream, else it'll leak a handle. Don't close + // the acceptor here, though, because get_handle() needs it to + // correctly allow removal from the reactor later. It gets closed + // in the destructor. + this->stream_.close (); + + return 0; +} + +u_short +My_Accept_Handler::port () +{ + return this->addr_.get_port_number(); +} + +long max_connected_port = 0; + +#if defined (ACE_HAS_IPV6) +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = reinterpret_cast<ACE_INET_Addr *> (arg); + + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + "::1"); + + ACE_SOCK_Stream cli_stream; + + ACE_SOCK_Connector con; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Connecting to port %d\n"), + server_addr.get_port_number())); + + // Initiate connection with server; don't wait forever + if (con.connect (cli_stream, + server_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + + return 0; + } + + // if connect succesful, what is the max port number we connected + // up to now. + int connected_port = server_addr.get_port_number (); + + if (connected_port > max_connected_port) + max_connected_port = connected_port; + + cli_stream.close (); + + return 0; +} +#endif /*ACE_HAS_IPV6*/ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Max_Default_Port_Test_IPV6")); + + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + +#if defined (ACE_HAS_IPV6) + u_short max_listened_port = 0; + + //Ports beyond 65279 were said to bad on NT sp 3. + for (u_short idx = USHRT_MAX; idx != USHRT_MAX - 300; --idx) + { + ACE_INET_Addr addr (idx, "::"); + + My_Accept_Handler *eh = new My_Accept_Handler (addr); + + + if ( ACE_Reactor::instance()->register_handler ( + eh, + ACE_Event_Handler::ACCEPT_MASK) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Failed to register event handler")), + 1); + } + + ACE_DEBUG ((LM_DEBUG, "Registered event handler at %d\n", idx)); + + ACE_Time_Value tv (1); + +#if defined (ACE_HAS_THREADS) + + if (ACE_Thread_Manager::instance ()->spawn_n + (1, + ACE_THR_FUNC (client), + reinterpret_cast<void *> (&addr), + THR_NEW_LWP | THR_DETACHED) == -1) + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("thread create failed")), + 1); + + ACE_Thread_Manager::instance ()->wait (); + +#else + ACE_UNUSED_ARG (client); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) only one thread may be run in a process on this platform\n%a"), + 1)); +#endif //ACE_HAS_THREADS + + if (ACE_Reactor::instance()->handle_events (tv) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Reactor::handle_events")), + 1); + } + + // see if I can register a reactor at this port. + if (eh->port () == idx) + { + if (idx > max_listened_port) + max_listened_port = idx; + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Test Fail, listening port %d\n"), + eh->port()), + 1); + } + + ACE_Reactor::instance()->remove_handler ( + eh, + ACE_Event_Handler::ACCEPT_MASK | + ACE_Event_Handler::DONT_CALL); + delete eh; + } + + ACE_DEBUG ((LM_DEBUG, + "Value of ACE_MAX_DEFAULT_PORT %d\n", + ACE_MAX_DEFAULT_PORT)); + + ACE_DEBUG ((LM_DEBUG, + "Highest port value I can listen at %d\n", + max_listened_port)); + + ACE_DEBUG ((LM_DEBUG, + "Highest port value I can connect to %d\n", + max_connected_port)); + + if ((max_listened_port == ACE_MAX_DEFAULT_PORT) && + (max_connected_port == ACE_MAX_DEFAULT_PORT)) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Valid ACE_MAX_DEFAULT_PORT value: %d\n"), + max_listened_port)); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Invalid ACE_MAX_DEFAULT_PORT ") + ACE_TEXT ("or %d port may be busy; got to %d\n"), + ACE_MAX_DEFAULT_PORT, max_listened_port)); + + } + +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Mem_Map_Test.cpp b/ACE/tests/Mem_Map_Test.cpp new file mode 100644 index 00000000000..207f5a1eb15 --- /dev/null +++ b/ACE/tests/Mem_Map_Test.cpp @@ -0,0 +1,286 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Mem_Map_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of ACE_Mem_Map to reverse a +// file. The test first creates a dummy file for testing, then +// reverses the file and then reverses it again to get back the +// original file. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Mem_Map.h" +#include "ace/ACE.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/OS_Memory.h" + +ACE_RCSID(tests, Mem_Map_Test, "Mem_Map_Test.cpp,v 4.36 2003/11/01 11:15:25 dhinton Exp") + +#if !defined (ACE_LACKS_MMAP) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; +static const int LINE_LENGTH = 10; +static const int NUM_LINES = 15; + +static void +reverse_file (ACE_HANDLE file_handle, + char *array, + size_t size) +{ + int count = 0; + // LynxOS 3.0.0/PowerPC needs the volatile qualifier, with -O2 + // optimization enabled and without ACE_HAS_INLINE. + volatile size_t i = size; + --i; + + if (array[i] == '\0') + array[i] = '\n'; + + while (i-- > 0) + { + if (array[i] == '\n') + { + ACE_OS::write (file_handle, array + i + 1, count); + ACE_OS::write (file_handle, ACE_TEXT ("\n"), 1); + count = 0; + } + else + count++; + } + ACE_OS::write (file_handle, array, count+1); +} + +static int +create_test_file (ACE_TCHAR *filename, int line_length, int num_lines) +{ + char *mybuf = 0; + + ACE_NEW_RETURN (mybuf, char[line_length + 1], -1); + const char *c = ACE_ALPHABET; + const char *d = c; +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + // For NTO has to applied to open the file, as Mem_Map can map only shared memory + ACE_Mem_Map mmap_4_open; + mmap_4_open.open (filename, O_RDWR | O_CREAT | O_TRUNC, ACE_DEFAULT_FILE_PERMS); + ACE_HANDLE file_handle = mmap_4_open.handle(); +#else + ACE_HANDLE file_handle = ACE_OS::open (filename, + O_RDWR | O_CREAT | O_TRUNC, + ACE_DEFAULT_FILE_PERMS); +#endif + if (file_handle == ACE_INVALID_HANDLE) + { + delete [] mybuf; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Open failed for %s\n"), + filename), + -1); + } + + for (int j = 0; j < num_lines; j++) + { + for (int i = 0; i < line_length; i++) + { + mybuf[i] = *c; + c++; + } + + mybuf[line_length] = '\0'; + + c = ++d; + + if (ACE_OS::write (file_handle, mybuf, line_length) != line_length) + { + delete [] mybuf; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p (%d) <%s>\n"), + ACE_TEXT ("Write to file failed:"), + errno, + filename), + -1); + } + + if (ACE_OS::write (file_handle, ACE_TEXT ("\n"), 1) != 1) + { + delete [] mybuf; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("write to file %s failed\n"), + filename), + -1); + } + } +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + mmap_4_open.close(); +#else + ACE_OS::close (file_handle); +#endif + + delete [] mybuf; + return 0; +} + +#endif /* !ACE_LACKS_MMAP */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Mem_Map_Test")); + +#if !defined (ACE_LACKS_MMAP) + +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + ACE_ERROR ((LM_INFO, + ACE_TEXT ("mmap on QNX Neutrino/VxWorks can map only shared memory files\n"))); +#endif + + // = Initialize the temporary variable names + + ACE_TCHAR test_file[MAXPATHLEN + 1]; + ACE_TCHAR temp_file1[MAXPATHLEN + 1]; + ACE_TCHAR temp_file2[MAXPATHLEN + 1]; + + // Get the temporary directory + // - 18 is for the filenames, ace_mem_map_temp_1 is the longest + if (ACE::get_temp_dir (test_file, MAXPATHLEN - 18) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Temporary path too long\n")), + -1); + + // Copy the temp directory to the other variables + ACE_OS::strcpy (temp_file1, test_file); + ACE_OS::strcpy (temp_file2, test_file); + + // Add the filenames to the end + ACE_OS::strcat (test_file, + ACE_TEXT ("ace_mem_map_test")); + ACE_OS::strcat (temp_file1, + ACE_TEXT ("ace_mem_map_temp_1")); + ACE_OS::strcat (temp_file2, + ACE_TEXT ("ace_mem_map_temp_2")); + + // First create a test file to work on + if (create_test_file (test_file, LINE_LENGTH, NUM_LINES) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Create test file failed\n")), + -1); + ACE_Mem_Map mmap; + + // First memory map the test file + if (mmap.map (test_file) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%n: %p %s\n%a"), + ACE_TEXT ("mmap"), + test_file), + -1); + + // Now create a temporary file for intermediate processing +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + ACE_Mem_Map mmap_4_open; + mmap_4_open.open(temp_file1, + O_RDWR | O_TRUNC | O_CREAT, + ACE_DEFAULT_FILE_PERMS); + ACE_HANDLE temp_file_handle = mmap_4_open.handle(); +#else + ACE_HANDLE temp_file_handle = ACE_OS::open (temp_file1, + O_RDWR | O_TRUNC | O_CREAT, + ACE_DEFAULT_FILE_PERMS); +#endif + + if (temp_file_handle == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Open failed\n")), + -1); + + // Reverse the original file and write the output to the temporary + // file. + reverse_file (temp_file_handle, + (char *) mmap.addr (), + mmap.size ()); +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + mmap_4_open.close(); +#else + ACE_OS::close (temp_file_handle); +#endif + + ACE_Mem_Map temp_mmap; + + // Now memory map the temporary file + if (temp_mmap.map (temp_file1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%n: %p %s\n%a"), + ACE_TEXT ("mmap"), + temp_file1), + -1); +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + mmap_4_open.open(temp_file2, + O_RDWR | O_TRUNC | O_CREAT, + ACE_DEFAULT_FILE_PERMS); + temp_file_handle = mmap_4_open.handle(); +#else + temp_file_handle = ACE_OS::open (temp_file2, + O_RDWR | O_TRUNC | O_CREAT, + ACE_DEFAULT_FILE_PERMS); +#endif + if ( temp_file_handle == ACE_INVALID_HANDLE) + + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Open failed\n")), -1); + + // Now reverse the temporary file and write everything to the second + // temporary file. + reverse_file (temp_file_handle, + (char *) temp_mmap.addr (), + temp_mmap.size ()); +#if defined (__QNXNTO__) || (defined (ACE_VXWORKS) && (ACE_VXWORKS <= 0x640)) + mmap_4_open.close(); +#else + ACE_OS::close (temp_file_handle); +#endif + + // Memory map the second temporary file + ACE_Mem_Map temp_mmap2; + + if (temp_mmap2.map (temp_file2) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%n: %p %s\n%a"), + ACE_TEXT ("mmap"), + temp_file2), + -1); + + // Now do a memcmp -- the orig file and the second temporary file + // should be identical. + ACE_ASSERT (ACE_OS::memcmp (temp_mmap2.addr (), + mmap.addr (), + mmap.size ()) == 0); + + // Delete the test file + mmap.remove (); + + // Delete ACE_TEMP_TEST_FILE + temp_mmap.remove (); + + // Delete ACE_TEMP_TEST_FILE_2 + temp_mmap2.remove (); + +#else /* !ACE_LACKS_MMAP */ + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("mmap is not supported on this platform\n"))); + +#endif /* !ACE_LACKS_MMAP */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Memcpy_Test.cpp b/ACE/tests/Memcpy_Test.cpp new file mode 100644 index 00000000000..46fb2c381e5 --- /dev/null +++ b/ACE/tests/Memcpy_Test.cpp @@ -0,0 +1,103 @@ +//============================================================================= +/** + * @file Memcpy_Test.cpp + * + * $Id$ + * + * @author Mike Marinez <mmartinez@oci.com> + * + * This test compares the performance of ACE_OS::memcpy with + * that of smemcpy which unrolls the memcpy loop upto size = 16. + * This test is also run via autoconf and if smemcpy is faster, + * ACE_HAS_MEMCPY_LOOP_UNROLL flag is set. + * + */ +//============================================================================= + +#include "ace/OS_NS_string.h" +#include "ace/High_Res_Timer.h" + +#include "test_config.h" + +void * +smemcpy (void *dest, const void *src, const size_t n) +{ + unsigned char *to = static_cast<unsigned char*> (dest); + const unsigned char *from = static_cast<const unsigned char*> (src); + // Unroll the loop... + switch (n) { + case 16: to[ 15] = from[ 15]; + case 15: to[ 14] = from[ 14]; + case 14: to[ 13] = from[ 13]; + case 13: to[ 12] = from[ 12]; + case 12: to[ 11] = from[ 11]; + case 11: to[ 10] = from[ 10]; + case 10: to[ 9] = from[ 9]; + case 9: to[ 8] = from[ 8]; + case 8: to[ 7] = from[ 7]; + case 7: to[ 6] = from[ 6]; + case 6: to[ 5] = from[ 5]; + case 5: to[ 4] = from[ 4]; + case 4: to[ 3] = from[ 3]; + case 3: to[ 2] = from[ 2]; + case 2: to[ 1] = from[ 1]; + case 1: to[ 0] = from[ 0]; + case 0: return dest; + default: return ACE_OS::memcpy (dest, src, n); + } +} + +void +testit (int type) +{ + char buffer[16]; + size_t size = 16; + + switch (type) + { + case 0: smemcpy ((void*)buffer, (void*)" THIS IS A TEST", size); break; + case 1: ACE_OS::memcpy ((void*)buffer, (void*)" THIS IS A TEST", size); break; + } + +} + +namespace { enum { ITERATIONS = 100000000 }; } + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Memcpy_Test")); + + //ACE_Time_Value start, now; + struct timeval start, now; + + for (int i = ITERATIONS; i > 0; --i) + testit (0); + + start = ACE_High_Res_Timer::gettimeofday_hr (); + for (int j = ITERATIONS; j > 0; --j) + testit (0); + + now = ACE_High_Res_Timer::gettimeofday_hr (); + + double fast = 1000000 *(now.tv_sec - start.tv_sec) + + now.tv_usec - start.tv_usec; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%f uSec per iteration for fast version.\n"), + fast / ITERATIONS)); + + start = ACE_High_Res_Timer::gettimeofday_hr (); + for (int k = ITERATIONS; k > 0; --k) + testit (1); + + now = ACE_High_Res_Timer::gettimeofday_hr (); + + double slow = 1000000 * (now.tv_sec-start.tv_sec) + now.tv_usec - start.tv_usec; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%f uSec per iteration for slow version.\n"), + slow / ITERATIONS)); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Message_Block_Test.cpp b/ACE/tests/Message_Block_Test.cpp new file mode 100644 index 00000000000..fa14d46ab2f --- /dev/null +++ b/ACE/tests/Message_Block_Test.cpp @@ -0,0 +1,412 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Message_Block_Test.cpp +// +// = DESCRIPTION +// This test program is a torture test that illustrates how +// <ACE_Message_Block> reference counting works in multi-threaded +// code. +// +// = AUTHOR +// Doug Schmidt <schmidt@cs.wustl.edu> and Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/Task.h" +#include "ace/Malloc_T.h" +#include "ace/Profile_Timer.h" +#include "ace/Free_List.h" + +ACE_RCSID (tests, + Message_Block_Test, + "$Id$") + +// Number of memory allocation strategies used in this test. +static const int ACE_ALLOC_STRATEGY_NO = 2; + +// Size of a memory block (multiple of ACE_MALLOC_ALIGN). +static const int ACE_ALLOC_SIZE = 5; + +// Amount of memory block preallocated. +static const size_t ACE_ALLOC_AMOUNT = 48; + +#if defined (ACE_HAS_THREADS) + +#include "ace/Lock_Adapter_T.h" +#include "ace/Synch_Traits.h" + +// Number of iterations to run the test. +static size_t n_iterations = ACE_MAX_ITERATIONS; + +static ACE_Lock_Adapter<ACE_SYNCH_MUTEX> lock_adapter_; +// Serialize access to <ACE_Message_Block> reference count, which will +// be decremented from multiple threads. + +class Worker_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Worker_Task (void); + // Activate the task. + + virtual int svc (void); + // Iterate <n_iterations> time printing off a message and "waiting" + // for all other threads to complete this iteration. + + virtual int put (ACE_Message_Block *mb, ACE_Time_Value *tv = 0); + // Allows the producer to pass messages to the <Message_Block>. + +private: + //FUZZ: disable check_for_lack_ACE_OS + virtual int close (u_long); + // Close hook. + //FUZZ: enable check_for_lack_ACE_OS +}; + +int +Worker_Task::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) close of worker\n"))); + return 0; +} + +// Simply enqueue the Worker_Task into the end of the queue. + +int +Worker_Task::put (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + return this->msg_queue ()->enqueue_prio (mb, tv); +} + +// Iterate <n_iterations> printing off a message and "waiting" for all +// other threads to complete this iteration. + +int +Worker_Task::svc (void) +{ + // The <ACE_Task::svc_run()> method automatically adds us to the + // process-wide <ACE_Thread_Manager> when the thread begins. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) starting svc() method\n"))); + + // Keep looping, reading a message out of the queue, until we get a + // message with a length == 0, which signals us to quit. + + for (int count = 0; ; count++) + { + ACE_Message_Block *mb = 0; + + if (-1 == this->msg_queue ()->dequeue_head (mb)) + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Worker_Task dequeue_head"))); + + size_t length = mb->length (); + + // If there's a next() Task then "logically" copy the message by + // calling <duplicate> and send it on down the pipeline. Note + // that this doesn't actually make a copy of the message + // contents (i.e., the Data_Block portion), it just makes a copy + // of the header and reference counts the data. + if (this->next () != 0) + { + if (-1 == this->put_next (mb->duplicate ())) + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Worker_Task put_next"))); + } + + // If there's no next() Task to send to, then we'll consume the + // message here. + else if (length > 0) + { + int current_count = ACE_OS::atoi ((ACE_TCHAR *)(mb->rd_ptr ())); + int i; + + if (count != current_count) + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%t) count from block should be %d ") + ACE_TEXT ("but is %d\n"), + count, current_count)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) enqueueing %d duplicates\n"), + current_count)); + + ACE_Message_Block *dup; + + // Enqueue <current_count> duplicates with msg_priority == 1. + for (i = current_count; i > 0; i--) + { + ACE_ALLOCATOR_RETURN (dup, + mb->duplicate (), + -1); + // Set the priority to be greater than "normal" + // messages. Therefore, all of these messages should go + // to the "front" of the queue, i.e., ahead of all the + // other messages that are being enqueued by other + // threads. + dup->msg_priority (ACE_DEFAULT_MESSAGE_BLOCK_PRIORITY + 1); + + int enqueue_prio_result = + this->msg_queue ()->enqueue_prio + (dup, + // Don't block indefinitely if we flow control... + (ACE_Time_Value *) &ACE_Time_Value::zero); + + if (enqueue_prio_result == -1) + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%t) Pass %d %p\n"), + i, + ACE_TEXT ("Worker_Task enqueue_prio"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) dequeueing %d duplicates\n"), + current_count)); + + // Dequeue the same <current_count> duplicates. + for (i = current_count; i > 0; i--) + { + if (-1 == this->msg_queue ()->dequeue_head (dup)) + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%t) Dup %d, %p\n"), + i, + ACE_TEXT ("Worker_Task dequeue dups"))); + if (count != ACE_OS::atoi ((ACE_TCHAR *)(dup->rd_ptr ()))) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) line %l, Dup %d, block's count ") + ACE_TEXT ("is %d but should be %d\n"), + i, + ACE_OS::atoi ((ACE_TCHAR *)(dup->rd_ptr ())), + count)); + if (0 != ACE_OS::strcmp ((ACE_TCHAR *)mb->rd_ptr (), + (ACE_TCHAR *)dup->rd_ptr ())) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Dup %d text is %s; ") + ACE_TEXT ("should be %s\n"), + i, + dup->rd_ptr (), + mb->rd_ptr ())); + if (dup->msg_priority () != ACE_DEFAULT_MESSAGE_BLOCK_PRIORITY + 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Dup %d block priority is %u; ") + ACE_TEXT ("should be %u\n"), + i, + (unsigned int)dup->msg_priority (), + (unsigned int)(ACE_DEFAULT_MESSAGE_BLOCK_PRIORITY + 1))); + dup->release (); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) in iteration %d, length = %d, prio = %d, text = \"%*s\"\n"), + count, + length, + mb->msg_priority (), + length - 2, // remove the trailing "\n\0" + mb->rd_ptr ())); + } + + // We're responsible for deallocating this. + mb->release (); + + if (length == 0) + { + //FUZZ: disable check_for_NULL + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) in iteration %d, queue len = %d, got NULL message, exiting\n"), + count, this->msg_queue ()->message_count ())); + //FUZZ: enable check_for_NULL + break; + } + } + + // Note that the ACE_Task::svc_run () method automatically removes + // us from the Thread_Manager when the thread exits. + return 0; +} + +Worker_Task::Worker_Task (void) +{ + // Make us an Active Object. + if (this->activate (THR_NEW_LWP) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("activate failed"))); +} + +static int +produce (Worker_Task &worker_task, + ACE_Allocator *alloc_strategy) +{ + ACE_Message_Block *mb; + int status; + + // Send <n_iteration> messages through the pipeline. + for (size_t count = 0; count < n_iterations; count++) + { + ACE_TCHAR buf[BUFSIZ]; + ACE_OS::sprintf (buf, ACE_SIZE_T_FORMAT_SPECIFIER, count); + + size_t n = (ACE_OS::strlen (buf) + 1) * sizeof (ACE_TCHAR); + + // Allocate a new message. + ACE_NEW_RETURN (mb, + ACE_Message_Block (n, // size + ACE_Message_Block::MB_DATA, // type + 0, // cont + 0, // data + alloc_strategy, // allocator + &lock_adapter_, // locking strategy + ACE_DEFAULT_MESSAGE_BLOCK_PRIORITY), // priority + -1); + + // Try once to copy in more than the block will hold; should yield an + // error with ENOSPC. + if (count == 0) + { + status = mb->copy ((char *) buf, n + 1); + if (status != -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) Copy %B bytes into %B byte block ") + ACE_TEXT ("should fail but didn't\n"), + n + 1, + n)); + else if (errno != ENOSPC) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) Copy into too-small block failed ") + ACE_TEXT ("but with %p; should be ENOSPC\n"), + ACE_TEXT ("wrong error"))); + } + else + ACE_DEBUG ((LM_INFO, + ACE_TEXT (" (%t) Copy too-long test succeeded\n"))); + } + // Copy buf into the Message_Block and update the wr_ptr (). + status = mb->copy ((char *) buf, n); + if (status != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) Copy to block should be good but %p\n"), + ACE_TEXT ("failed"))); + } + // Pass the message to the Worker_Task. + if (worker_task.put (mb, + // Don't block indefinitely if we flow control... + (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("put"))); + } + + // Send a shutdown message to the waiting threads and exit. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) sending shutdown message\n"))); + + ACE_NEW_RETURN (mb, + ACE_Message_Block (0, + ACE_Message_Block::MB_DATA, + 0, + 0, + alloc_strategy, + &lock_adapter_), + -1); + + if (worker_task.put (mb) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("put"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) end producer\n"))); + return 0; +} + +typedef ACE_TCHAR MEMORY_CHUNK[ACE_MALLOC_ALIGN * ACE_ALLOC_SIZE]; + +ACE_Cached_Allocator<MEMORY_CHUNK, + ACE_SYNCH_MUTEX> + mem_allocator (ACE_ALLOC_AMOUNT); +struct alloc_struct_type +{ + ACE_Allocator *strategy_; + const ACE_TCHAR *name_; + ACE_Profile_Timer::ACE_Elapsed_Time et_; +}; + +alloc_struct_type alloc_struct[ACE_ALLOC_STRATEGY_NO] = +{ + { 0, ACE_TEXT ("Default"), {0,0,0} }, + { &mem_allocator, ACE_TEXT ("Cached Memory"), {0,0,0} } +}; + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Message_Block_Test")); +#if defined (ACE_HAS_THREADS) + int n_threads = ACE_MAX_THREADS; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) threads = %d\n"), n_threads)); + + ACE_Profile_Timer ptime; + + int i; + + for (i = 0; i < ACE_ALLOC_STRATEGY_NO; i++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Start Message_Block_Test using %s allocation strategy\n"), + alloc_struct[i].name_)); + + // Create the worker tasks. + Worker_Task worker_task[ACE_MAX_THREADS] ; + + // Link all the tasks together into a simple pipeline. + for (size_t j = 1; j < ACE_MAX_THREADS; j++) + worker_task[j - 1].next (&worker_task[j]); + + ptime.start (); + // Generate messages and pass them through the pipeline. + produce (worker_task[0], alloc_struct[i].strategy_); + + // Wait for all the threads to reach their exit point. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) waiting for worker tasks to finish...\n"))); + + ACE_Thread_Manager::instance ()->wait (); + ptime.stop (); + ptime.elapsed_time (alloc_struct[i].et_); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) destroying worker tasks\n"))); + } + + for (i = 0; i < ACE_ALLOC_STRATEGY_NO; i++) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Elapsed time using %s allocation strategy: %f sec\n"), + alloc_struct[i].name_, + alloc_struct[i].et_.real_time)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Exiting...\n"))); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Message_Queue_Notifications_Test.cpp b/ACE/tests/Message_Queue_Notifications_Test.cpp new file mode 100644 index 00000000000..51fe8c261b3 --- /dev/null +++ b/ACE/tests/Message_Queue_Notifications_Test.cpp @@ -0,0 +1,367 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Message_Queue_Notification_Test.cpp +// +// = DESCRIPTION +// There are two tests that test 2 different notification +// mechanisms in Message Queue. +// +// The first test illustrates the notification mechanisms in +// Message_Queue and its integration with Reactor. +// +// Note the following things about this part of the test: +// +// 1. Multiple threads are not required. +// 2. You do not have to explicitly notify the Reactor +// 3. This code will work the same with any Reactor Implementation +// 4. handle_input, handle_exception, handle_output are the only +// callbacks supported by this mechanism +// 5. The notification mechanism need not notify the Reactor. You can +// write your own strategy classes that can do whatever application +// specific behavior you want. +// +// The second test also makes sure the high/low water mark +// signaling mechanism works flawlessly. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> and Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/Task.h" +#include "ace/Reactor_Notification_Strategy.h" +#include "ace/Atomic_Op.h" +#include "ace/Barrier.h" +#include "ace/Synch_Traits.h" +#include "ace/Null_Condition.h" +#include "ace/Null_Mutex.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Message_Queue_Notifications_Test, "$Id$") + +static int iterations = 10; + +static const size_t worker_threads = 2; +static const char * default_message = "ACE RULES"; +static const size_t default_high_water_mark = 20; +static const size_t default_low_water_mark = 10; +static const int watermark_iterations = 2 * default_high_water_mark; + +class Message_Handler : public ACE_Task<ACE_NULL_SYNCH> +{ + // = TITLE + // This class implements a notification strategy for the Reactor. +public: + // = Initialization and termination. + Message_Handler (ACE_Reactor &reactor); + // Constructor. + + // = Demuxing hooks. + virtual int handle_input (ACE_HANDLE); + virtual int handle_output (ACE_HANDLE fd = ACE_INVALID_HANDLE); + virtual int handle_exception (ACE_HANDLE fd = ACE_INVALID_HANDLE); + +private: + int process_message (void); + void make_message (void); + + ACE_Reactor_Notification_Strategy notification_strategy_; +}; + +class Watermark_Test : public ACE_Task<ACE_SYNCH> +{ + // = TITLE + // This class test the correct functioning of build-in flow + // control machanism in ACE_Task. +public: + Watermark_Test (void); + + virtual int svc (void); + + int consumer (void); + int producer (void); + int put_message (ACE_Time_Value* timeout = 0); + int get_message (void); + void print_producer_debug_message (void); + +private: + const size_t len_; + const size_t hwm_; + const size_t lwm_; + ACE_Atomic_Op <ACE_SYNCH_MUTEX, int> role_; +#if defined (ACE_HAS_THREADS) + ACE_Barrier mq_full_; + ACE_Barrier mq_low_water_mark_hit_; +#endif /* ACE_HAS_THREADS */ +}; + +Message_Handler::Message_Handler (ACE_Reactor &reactor) + // First time handle_input will be called + : notification_strategy_ (&reactor, + this, + ACE_Event_Handler::READ_MASK) +{ + this->msg_queue ()->notification_strategy (&this->notification_strategy_); + this->make_message (); +} + +int +Message_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Message_Handler::handle_input\n"))); + + // Next time handle_output will be called. + this->notification_strategy_.mask (ACE_Event_Handler::WRITE_MASK); + + return process_message (); +} + +int +Message_Handler::handle_output (ACE_HANDLE fd) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Message_Handler::handle_output\n"))); + ACE_UNUSED_ARG (fd); + + // Next time handle_exception will be called. + this->notification_strategy_.mask (ACE_Event_Handler::EXCEPT_MASK); + + return process_message (); +} + +int +Message_Handler::handle_exception (ACE_HANDLE fd) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Message_Handler::handle_exception\n"))); + ACE_UNUSED_ARG (fd); + + // Next time handle_input will be called. + this->notification_strategy_.mask (ACE_Event_Handler::READ_MASK); + + return this->process_message (); +} + +int +Message_Handler::process_message (void) +{ + ACE_Message_Block *mb; + + if (this->getq (mb, + (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + -1); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("message received = %s\n"), + mb->rd_ptr ())); + mb->release (); + } + + this->make_message (); + return 0; +} + +void +Message_Handler::make_message (void) +{ + if (--iterations > 0) + { + ACE_Message_Block *mb; + ACE_NEW (mb, + ACE_Message_Block ((char *) ACE_TEXT ("hello"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("sending message\n"))); + this->putq (mb); + } +} + +Watermark_Test::Watermark_Test (void) + : len_ (ACE_OS::strlen (default_message) + 1), + hwm_ (this->len_ * default_high_water_mark), + lwm_ (this->len_ * default_low_water_mark), + role_ (0) +#if defined (ACE_HAS_THREADS) + , mq_full_ (worker_threads), + mq_low_water_mark_hit_ (worker_threads) +#endif /* ACE_HAS_THREADS */ +{ + this->water_marks (ACE_IO_Cntl_Msg::SET_LWM, + this->lwm_); + this->water_marks (ACE_IO_Cntl_Msg::SET_HWM, + this->hwm_); +} + +int +Watermark_Test::producer (void) +{ + int i = watermark_iterations; + + for (ssize_t hwm = this->hwm_; + hwm >= 0 ; + hwm -= this->len_) + { + this->put_message (); + this->print_producer_debug_message (); + i--; + if (this->msg_queue ()->is_full ()) + break; + + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Producer: High water mark hit ---- \n"))); + + ACE_MT (this->mq_full_.wait ()); + + // The following put_message should block until the message queue + // has dropped under the lwm. + this->put_message (); + + ACE_ASSERT (this->msg_queue ()-> message_bytes () <= this->lwm_ + this->len_); + + this->print_producer_debug_message (); + + for (i--; i >= 0 ; i--) + { + this->put_message (); + this->print_producer_debug_message (); + } + + return 0; +} + +int +Watermark_Test::consumer (void) +{ + ACE_MT (this->mq_full_.wait ()); + + ACE_OS::sleep (1); + + // Let producer proceed and block in putq. + + for (int i = watermark_iterations; i >= 0; i--) + { + this->get_message (); + ACE_OS::sleep (0); + } + + return 0; +} + +int +Watermark_Test::get_message (void) +{ + ACE_Message_Block *mb; + + if (this->getq (mb) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + -1); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Consumer: message size = %3d, ") + ACE_TEXT ("message count = %3d\n"), + this->msg_queue ()-> message_bytes (), + this->msg_queue ()-> message_count ())); + mb->release (); + } + + return 0; +} + +int +Watermark_Test::put_message (ACE_Time_Value *timeout) +{ + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, + ACE_Message_Block (default_message, + this->len_), + -1); + + return this->putq (mb, timeout); +} + +void +Watermark_Test::print_producer_debug_message (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Producer: message size = %3d, ") + ACE_TEXT ("message count = %3d\n"), + this->msg_queue ()-> message_bytes (), + this->msg_queue ()-> message_count ())); +} + +int +Watermark_Test::svc (void) +{ + // this->role_ is an Atomic_Op object. + int role = this->role_++; + + switch (role) + { + case 0: + this->producer (); + break; + case 1: + this->consumer (); + break; + default: + break; + } + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Message_Queue_Notifications_Test")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Starting message queue reactive notification test...\n"))); + + ACE_Reactor reactor; + Message_Handler mh (reactor); + + while (iterations > 0) + reactor.handle_events (); + +#if defined (ACE_HAS_THREADS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Starting message queue watermark test...\n"))); + Watermark_Test watermark_test; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("High water mark is %d\n") + ACE_TEXT ("Low water mark is %d\n"), + default_high_water_mark, + default_low_water_mark)); + + watermark_test.activate (THR_NEW_LWP, + worker_threads); + + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Message queue watermark test not performed because threads are not supported\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Message_Queue_Test.cpp b/ACE/tests/Message_Queue_Test.cpp new file mode 100644 index 00000000000..c359807c47c --- /dev/null +++ b/ACE/tests/Message_Queue_Test.cpp @@ -0,0 +1,690 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Message_Queue_Test.cpp +// +// = DESCRIPTION +// This is: +// 0) a test that ensures key ACE_Message_Queue features are +// working properly, including timeouts and priorities. +// 1) a simple test of the ACE_Message_Queue that illustrates how to +// use the forward and reverse iterators; +// 2) a simple performance measurement test for both single-threaded +// (null synch) and thread-safe ACE_Message_Queues, and +// ACE_Message_Queue_Vx, which wraps VxWorks message queues; and +// 3) a test/usage example of ACE_Message_Queue_Vx. +// +// = AUTHORS +// Irfan Pyarali <irfan@cs.wustl.edu>, +// David L. Levine <levine@cs.wustl.edu>, and +// Douglas C. Schmidt <schmidt@vanderbilt.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread_Manager.h" +#include "ace/Message_Queue.h" +#include "ace/Message_Queue_NT.h" +#include "ace/Message_Queue_Vx.h" +#include "ace/Synch_Traits.h" +#include "ace/Null_Mutex.h" +#include "ace/Null_Condition.h" +#include "ace/High_Res_Timer.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_time.h" + +ACE_RCSID(tests, Message_Queue_Test, "$Id$") + +const ACE_TCHAR usage[] = ACE_TEXT ("usage: Message_Queue_Test <number of messages>\n"); + +typedef ACE_Message_Queue<ACE_NULL_SYNCH> QUEUE; +typedef ACE_Message_Queue_Iterator<ACE_NULL_SYNCH> ITERATOR; +typedef ACE_Message_Queue_Reverse_Iterator<ACE_NULL_SYNCH> REVERSE_ITERATOR; + +static const int MAX_MESSAGES = 10000; +static const int MAX_MESSAGE_SIZE = 32; +static const char test_message[] = "ACE_Message_Queue Test Message"; + +static int max_messages = MAX_MESSAGES; + +// Dynamically allocate to avoid a static. +static ACE_High_Res_Timer *timer = 0; + +#if defined (ACE_HAS_THREADS) +typedef ACE_Message_Queue<ACE_MT_SYNCH> SYNCH_QUEUE; + +struct Queue_Wrapper +{ + // = TITLE + // Container for data passed to sender and receiver in + // performance test. + // + // = DESCRIPTION + // For use in multithreaded performance test. + + ACE_Message_Queue_Base *q_; + // The message queue. + + ACE_Message_Block **send_block_; + // Pointer to messages blocks for sender to send to reciever. + + Queue_Wrapper (void) + : q_ (0), send_block_ (0) + { + } + // Default constructor. +}; + +#endif /* ACE_HAS_THREADS */ + +#if !defined (VXWORKS) +static int +iterator_test (void) +{ + const int ITERATIONS = 5; + ACE_TCHAR buffer[ITERATIONS][BUFSIZ]; + // Use queue size from of 32 Kb (more if using wide-char), instead of the + // default of 16 Kb (defined by ACE_Message_Queue_Base::DEFAULT_HWM), + // so that the test runs on machines with 8Kb pagesizes. +#if !defined(_UNICOS) + // QUEUE queue (32 * 1024 * sizeof (ACE_TCHAR)); + QUEUE queue (sizeof(buffer)); +#else + // this works on the Cray, where BUFSIZ is defined as 32Kb + QUEUE queue (ITERATIONS * BUFSIZ - 1); +#endif + + int i; + + for (i = 0; i < ITERATIONS; i++) + { + ACE_OS::sprintf (buffer[i], + ACE_TEXT ("%d"), + i + 1); + + ACE_Message_Block *entry; + ACE_NEW_RETURN (entry, + ACE_Message_Block ((char *) buffer[i], + sizeof buffer[i]), + -1); + + if (queue.is_full ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("QUEUE:: the message queue is full on iteration %u!\n"), + i + 1), + -1); + + if (queue.enqueue (entry) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("QUEUE::enqueue\n")), + -1); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nForward Iterations\n"))); + { + ITERATOR iterator (queue); + + for (ACE_Message_Block *entry = 0; + iterator.next (entry) != 0; + iterator.advance ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s\n"), + entry->base ())); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nReverse Iterations\n"))); + { + REVERSE_ITERATOR iterator (queue); + + for (ACE_Message_Block *entry = 0; + iterator.next (entry) != 0; + iterator.advance ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s\n"), + entry->base ())); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nForward Iterations\n"))); + { + QUEUE::ITERATOR iterator (queue); + + for (ACE_Message_Block *entry = 0; + iterator.next (entry) != 0; + iterator.advance ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s\n"), + entry->base ())); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nReverse Iterations\n"))); + { + QUEUE::REVERSE_ITERATOR iterator (queue); + + for (ACE_Message_Block *entry = 0; + iterator.next (entry) != 0; + iterator.advance ()) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s\n"), + entry->base ())); + } + + return 0; +} +#endif /* ! VXWORKS */ + +#if defined (ACE_HAS_THREADS) + +static int +chained_block_test (void) +{ + + QUEUE q; + const char * s = "123456789"; // Will be length 10 when copied to block + const size_t slen = 10; + const size_t num_blks = 10; + ACE_Message_Block b[num_blks]; + size_t i; + int status = 0; + + for (i = 0; i < num_blks; ++i) + { + b[i].init (slen); + b[i].copy (s); + } + + // Test enqueueing single and chained blocks and be sure they end up with + // the proper enqueued block count and sizes. Then be sure they are dequeued + // in the proper order. + b[0].next (&b[1]); + b[1].next (&b[2]); + // b[3] and b[4] are unchained. + b[5].next (&b[6]); + b[6].next (&b[7]); + b[7].next (&b[8]); + // b[9] is unchained + q.enqueue_tail (&b[3]); + q.enqueue_tail (&b[4]); + int num = q.enqueue_head (&b[0]); + if (num != 5) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Chained enqueue expected 5; has %d\n"), + num)); + status = -1; + } + num = q.enqueue_tail (&b[5]); + if (num != 9) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Chained enqueue expected 9; has %d\n"), + num)); + status = -1; + } + num = q.enqueue_tail (&b[9]); + if (num != 10) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Chained enqueue expected 10; has %d\n"), + num)); + status = -1; + } + size_t msgs, bytes; + msgs = q.message_count (); + bytes = q.message_bytes (); + if (msgs != 10 || bytes != 100) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Chained enqueue totals: %d msgs, %d bytes; ") + ACE_TEXT ("should be 10 msgs, 100 bytes\n"), + (int)msgs, (int)bytes)); + status = -1; + } + + // Now see if we can dequeue them, checking the order. + ACE_Time_Value nowait (ACE_OS::gettimeofday ()); + ACE_Message_Block *bp; + int qstat; + for (i = 0; i < num_blks; ++i) + { + qstat = q.dequeue_head (bp, &nowait); + if (qstat == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Checking chained blocks, pass %d: %p\n"), + (int)i, ACE_TEXT ("dequeue_head"))); + status = -1; + } + else if (bp != &b[i]) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Checking chained blocks, pass %d: ") + ACE_TEXT ("block out of order\n"), + (int)i)); + status = -1; + } + } + + if (status == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Chained block test OK\n"))); + return status; +} + +static int +single_thread_performance_test (int queue_type = 0) +{ + const char test_message[] = + "ACE_Message_Queue Test Message"; + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue<ACE_NULL_SYNCH>, single thread"); + int i = 0; + + // Create a message queue. + ACE_Message_Queue_Base *msgq = 0; + + if (queue_type == 0) + ACE_NEW_RETURN (msgq, + QUEUE, + -1); +#if defined (VXWORKS) + else + { + ACE_NEW_RETURN (msgq, + ACE_Message_Queue_Vx (max_messages, + MAX_MESSAGE_SIZE), + -1); + message = "ACE_Message_Queue_Vx, single thread test"; + } +#elif defined (ACE_WIN32) && defined (ACE_HAS_WIN32_OVERLAPPED_IO) + else + { + ACE_NEW_RETURN (msgq, + ACE_Message_Queue_NT, + -1); + message = ACE_TEXT ("ACE_Message_Queue_NT, single thread test"); + } +#endif /* VXWORKS */ + + // Create the messages. Allocate off the heap in case messages + // is large relative to the amount of stack space available. + ACE_Message_Block **send_block = 0; + ACE_NEW_RETURN (send_block, + ACE_Message_Block *[max_messages], + -1); + + for (i = 0; i < max_messages; ++i) + ACE_NEW_RETURN (send_block[i], + ACE_Message_Block (test_message, + MAX_MESSAGE_SIZE), + -1); + + ACE_Message_Block **receive_block_p = 0; + ACE_NEW_RETURN (receive_block_p, + ACE_Message_Block *[max_messages], + -1); + +#if defined (VXWORKS) + // Set up blocks to receive the messages. Allocate these off the + // heap in case messages is large relative to the amount of + // stack space available. + ACE_Message_Block *receive_block; + ACE_NEW_RETURN (receive_block, + ACE_Message_Block[max_messages], + -1); + + for (i = 0; i < max_messages; ++i) + { + receive_block[i].init (MAX_MESSAGE_SIZE); + + // For VxWorks Message Queues, the receive block pointer must be + // assigned. It will be used by dequeue_head (). + receive_block_p[i] = &receive_block[i]; + } +#endif /* VXWORKS */ + + timer->start (); + + // Send/receive the messages. + for (i = 0; i < max_messages; ++i) + { + if (msgq->enqueue_tail (send_block[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue")), + -1); + + if (msgq->dequeue_head (receive_block_p[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + -1); + } + + timer->stop (); + + ACE_Time_Value tv; + timer->elapsed_time (tv); + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%s: %u messages took %u msec (%f msec/message)\n"), + message, + max_messages, + tv.msec (), + (double) tv.msec () / max_messages)); + timer->reset (); + + delete [] receive_block_p; +#if defined (VXWORKS) + delete [] receive_block; +#endif /* VXWORKS */ + + for (i = 0; i < max_messages; ++i) + delete send_block[i]; + delete [] send_block; + delete msgq; + + return 0; +} + +static void * +receiver (void *arg) +{ + Queue_Wrapper *queue_wrapper = + reinterpret_cast<Queue_Wrapper *> (arg); + int i; + + ACE_Message_Block **receive_block_p = 0; + ACE_NEW_RETURN (receive_block_p, + ACE_Message_Block *[max_messages], + (void *) -1); + +#if defined (VXWORKS) + // Set up blocks to receive the messages. Allocate these off the + // heap in case messages is large relative to the amount of stack + // space available. + ACE_Message_Block *receive_block; + ACE_NEW_RETURN (receive_block, + ACE_Message_Block[max_messages], + (void *) -1); + + for (i = 0; i < max_messages; ++i) + { + receive_block[i].init (MAX_MESSAGE_SIZE); + + // For VxWorks Message Queues, the receive block pointer must be + // assigned. It will be used by <dequeue_head>. + receive_block_p[i] = &receive_block[i]; + } +#endif /* VXWORKS */ + + for (i = 0; i < max_messages; ++i) + if (queue_wrapper->q_->dequeue_head (receive_block_p[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + 0); + timer->stop (); + + delete [] receive_block_p; +#if defined (VXWORKS) + delete [] receive_block; +#endif /* VXWORKS */ + + return 0; +} + +static void * +sender (void *arg) +{ + Queue_Wrapper *queue_wrapper = + reinterpret_cast<Queue_Wrapper *> (arg); + int i; + + timer->start (); + + // Send the messages. + for (i = 0; i < max_messages; ++i) + if (queue_wrapper->q_-> + enqueue_tail (queue_wrapper->send_block_[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue")), + 0); + return 0; +} + +static +int +performance_test (int queue_type = 0) +{ + Queue_Wrapper queue_wrapper; + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue<ACE_SYNCH>"); + int i = 0; + + // Create the messages. Allocate off the heap in case messages is + // large relative to the amount of stack space available. Allocate + // it here instead of in the sender, so that we can delete it after + // the _receiver_ is done. + ACE_Message_Block **send_block = 0; + ACE_NEW_RETURN (send_block, + ACE_Message_Block *[max_messages], + -1); + + for (i = 0; i < max_messages; ++i) + ACE_NEW_RETURN (send_block[i], + ACE_Message_Block (test_message, + MAX_MESSAGE_SIZE), + -1); + + queue_wrapper.send_block_ = send_block; + + if (queue_type == 0) + ACE_NEW_RETURN (queue_wrapper.q_, + SYNCH_QUEUE, + -1); +#if defined (VXWORKS) + else + { + ACE_NEW_RETURN (queue_wrapper.q_, + ACE_Message_Queue_Vx (max_messages, + MAX_MESSAGE_SIZE), + -1); + message = "ACE_Message_Queue_Vx"; + } +#elif defined (ACE_WIN32) && defined (ACE_HAS_WIN32_OVERLAPPED_IO) + else + { + ACE_NEW_RETURN (queue_wrapper.q_, + ACE_Message_Queue_NT, + -1); + message = ACE_TEXT ("ACE_Message_Queue_NT"); + } +#endif /* VXWORKS */ + + if (ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) sender, + &queue_wrapper, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning sender thread")), + -1); + + if (ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) receiver, + &queue_wrapper, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning receiver thread")), + -1); + + ACE_Thread_Manager::instance ()->wait (); + ACE_Time_Value tv; + timer->elapsed_time (tv); + ACE_DEBUG ((LM_INFO, ACE_TEXT ("%s: %u messages took %u msec (%f msec/message)\n"), + message, + max_messages, + tv.msec (), + (double) tv.msec () / max_messages)); + timer->reset (); + + delete queue_wrapper.q_; + queue_wrapper.q_ = 0; + + for (i = 0; i < max_messages; ++i) + delete send_block[i]; + delete [] send_block; + + return 0; +} + +// Ensure that the timedout dequeue_head() sets errno code properly. + +static int +timeout_test (void) +{ + SYNCH_QUEUE mq; + int status = 0; + + if (!mq.is_empty ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("New queue is not empty!\n"))); + status = 1; + } + else + { + ACE_Message_Block *b; + ACE_Time_Value tv (ACE_OS::gettimeofday ()); // Now + + if (mq.dequeue_head (b, &tv) != -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Dequeued from empty queue!\n"))); + status = 1; + } + else if (errno != EWOULDBLOCK) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Dequeue timeout should be EWOULDBLOCK, got"))); + status = 1; + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Timed dequeue test: OK\n"))); + status = 0; // All is well + } + } + + return status; +} +#endif /* ACE_HAS_THREADS */ + +// Check to make sure that dequeue_prio() respects FIFO ordering. +// @@ At some point, this function should be enhanced to do a more +// thorough check... + +static int +prio_test (void) +{ + const char S1[] = "first"; + const char S2[] = "second"; + const int PRIORITY = 50; + QUEUE mq; + int status; + + ACE_Message_Block mb1 (S1, sizeof S1, PRIORITY); + ACE_Message_Block mb2 (S2, sizeof S2, PRIORITY); + + mq.enqueue_prio (&mb1); + mq.enqueue_prio (&mb2); + + ACE_Message_Block *mb1p; + ACE_Message_Block *mb2p; + + mq.dequeue_prio (mb1p); + mq.dequeue_prio (mb2p); + + ACE_DEBUG ((LM_DEBUG, "message 1 = %s\nmessage 2 = %s\n", + mb1p->rd_ptr (), + mb2p->rd_ptr ())); + + if (ACE_OS_String::strcmp (mb1p->rd_ptr (), S1) == 0 + && ACE_OS_String::strcmp (mb2p->rd_ptr (), S2) == 0) + status = 0; + else + status = 1; + + return status; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Message_Queue_Test")); + + if (argc == 2) + if (!ACE_OS::strcmp (argv[1], ACE_TEXT ("-?"))) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s/n"), + usage)); + else + max_messages = ACE_OS::atoi (argv[1]); + + int status = prio_test (); + +#if !defined (VXWORKS) + // The iterator test occasionally causes a page fault or a hang on + // VxWorks. + if (status == 0) + status = iterator_test (); +#endif /* ! VXWORKS */ + + ACE_NEW_RETURN (timer, + ACE_High_Res_Timer, + -1); + +#if defined (ACE_HAS_THREADS) + if (status == 0) + status = timeout_test (); + + if (status == 0) + status = chained_block_test (); + + if (status == 0) + status = single_thread_performance_test (); + +# if defined (VXWORKS) || defined (ACE_HAS_WIN32_OVERLAPPED_IO) + // Test ACE_Message_Queue_Vx. or ACE_Message_Queue_NT + if (status == 0) + status = single_thread_performance_test (1); +# endif /* VXWORKS */ + + if (status == 0) + status = performance_test (); + +# if defined (VXWORKS) || defined (ACE_HAS_WIN32_OVERLAPPED_IO) + // Test ACE_Message_Queue_Vx or ACE_Message_Queue_NT + if (status == 0) + status = performance_test (1); +# endif /* VXWORKS */ +#endif /* ACE_HAS_THREADS */ + + if (status != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test failed"))); + delete timer; + timer = 0; + + ACE_END_TEST; + return status; +} + diff --git a/ACE/tests/Message_Queue_Test_Ex.cpp b/ACE/tests/Message_Queue_Test_Ex.cpp new file mode 100644 index 00000000000..af0385a923e --- /dev/null +++ b/ACE/tests/Message_Queue_Test_Ex.cpp @@ -0,0 +1,778 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Message_Queue_Test_Ex.cpp +// +// = DESCRIPTION +// This is: +// 1. A simple test of the ACE_Message_Queue_Ex that executes +// a performance measurement test for both single-threaded +// (null synch) and thread-safe ACE_Message_Queue_Ex +// instantiations. +// 2. An example of using a user-defined class to parameterize +// ACE_Message_Queue_Ex. +// +// = AUTHORS +// Michael Vitlo <mvitalo@sprynet.com>, copied the code from: +// Irfan Pyarali <irfan@cs.wustl.edu> and +// David L. Levine <levine@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread_Manager.h" + +#include "ace/Auto_Ptr.h" +#include "ace/Message_Queue.h" +#include "ace/Synch_Traits.h" +#include "ace/Null_Mutex.h" +#include "ace/Null_Condition.h" +#include "ace/High_Res_Timer.h" +#include "ace/Message_Block.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Barrier.h" +#include "Message_Queue_Test_Ex.h" // Declares User_Class + +const ACE_TCHAR usage[] = + ACE_TEXT ("usage: Message_Queue_Test_Ex <number of messages>\n"); + +typedef ACE_Message_Queue_Ex<User_Class, ACE_NULL_SYNCH> QUEUE; + +static const int MAX_MESSAGES = 10000; +static const char test_message[] = "ACE_Message_Queue_Ex Test Message"; + +static int max_messages = MAX_MESSAGES; +static int chain_limit = 4; +static ACE_Barrier tester_barrier (2); + +// Dynamically allocate to avoid a static. +static ACE_High_Res_Timer *timer = 0; + +// Helper printing function +static void +print_message (const ACE_TCHAR *message) +{ + ACE_Time_Value tv; + timer->elapsed_time (tv); + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%s: %u messages took %u msec (%f msec/message)\n"), + message, + max_messages, + tv.msec (), + (double) tv.msec () / max_messages)); +} + +#if defined (ACE_HAS_THREADS) +typedef ACE_Message_Queue_Ex<User_Class, ACE_MT_SYNCH> SYNCH_QUEUE; + +struct Queue_Wrapper +{ + // = TITLE + // Container for data passed to sender and receiver in + // performance test. + // + // = DESCRIPTION + // For use in multithreaded performance test. + + SYNCH_QUEUE *q_; + // The message queue. + + User_Class **send_block_; + // Pointer to messages blocks for sender to send to reciever. + + Queue_Wrapper (void) + : q_ (0), send_block_ (0) + { + } + // Default constructor. +}; + +struct MQ_Ex_N_Tester_Wrapper +{ + // = TITLE + // Container for data passed to sender in the MQ_Ex_N_Tester + // performance test. + // + // = DESCRIPTION + // For use in multithreaded performance test. + MQ_Ex_N_Tester *tester_; + User_Class *head_send_block_; +}; + +#endif /* ACE_HAS_THREADS */ + +// Encapsulates the sent messages creation and destruction +struct Send_Messages +{ + Send_Messages (int number_of_messages, int chain_limit): + send_block_ (0), + number_of_messages_ (number_of_messages), + chain_limit_ (chain_limit) + { + } + + int create_messages (const char test_message[]) + { + int limit = this->number_of_messages_ / this->chain_limit_; + ACE_NEW_RETURN (this->send_block_, + User_Class *[limit], + -1); + + int i, j; + for (i = 0; i < limit; ++i) + { + User_Class *&temp1 = this->send_block_[i]; + ACE_NEW_RETURN (temp1, + User_Class (test_message), + -1); + User_Class *tail = temp1; + for (j = 1; j < this->chain_limit_; ++j) + { + User_Class *temp2 = 0; + ACE_NEW_RETURN (temp2, + User_Class (test_message), + -1); + tail->next (temp2); + tail = temp2; + } + } + this->head_send_block_ = this->send_block_[0]; + return 0; + } + + ~Send_Messages () + { + int j, i = 0; + int limit = this->number_of_messages_ / this->chain_limit_; + for (; i < limit; ++i) + { + User_Class *&temp1 = this->send_block_[i]; + for (j = 0; j < this->chain_limit_; ++j) + { + User_Class *temp2 = temp1->next (); + delete temp1; + temp1 = temp2; + } + } + delete [] this->send_block_; + } + + User_Class * head_send_block_; + User_Class ** send_block_; + int number_of_messages_; + int chain_limit_; +}; + +// Encapsulates the received messages creation and destruction +struct Receive_Messages +{ + Receive_Messages (int number_of_messages) : + receive_block_ (0), + number_of_messages_ (number_of_messages) + { + } + + int create (void) + { + ACE_NEW_RETURN (this->receive_block_, + User_Class *[this->number_of_messages_], + -1); + return 0; + } + + ~Receive_Messages () + { + delete [] this->receive_block_; + } + + User_Class **receive_block_; + int number_of_messages_; +}; + +static int +single_thread_performance_test (void) +{ + const char test_message[] = + "ACE_Message_Queue_Ex Test Message"; + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue_Ex<ACE_NULL_SYNCH>, single thread"); + + // Create a message queue. + QUEUE *msgq = 0; + + ACE_NEW_RETURN (msgq, + QUEUE, + -1); + + // Create the messages. Allocate off the heap in case messages is + // large relative to the amount of stack space available. + User_Class **send_block = 0; + ACE_NEW_RETURN (send_block, + User_Class *[max_messages], + -1); + + int i = 0; + + for (i = 0; i < max_messages; ++i) + ACE_NEW_RETURN (send_block[i], + User_Class (test_message), + -1); + + User_Class **receive_block_p = 0; + ACE_NEW_RETURN (receive_block_p, + User_Class *[max_messages], + -1); + + timer->start (); + + // Send/receive the messages. + for (i = 0; i < max_messages; ++i) + { + if (msgq->enqueue_tail (send_block[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue")), + -1); + + if (msgq->dequeue_head (receive_block_p[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + -1); + } + + timer->stop (); + print_message (message); + timer->reset (); + + delete [] receive_block_p; + + for (i = 0; i < max_messages; ++i) + delete send_block[i]; + delete [] send_block; + delete msgq; + + return 0; +} + +int +MQ_Ex_N_Tester::single_thread_performance_test (void) +{ + // Create the messages. Allocate off the heap in case messages is + // large relative to the amount of stack space available. + + if ((0 != this->test_enqueue_tail ()) || + (0 != this->test_enqueue_head ()) ) + { + return -1; + } + + return 0; +} + +int +MQ_Ex_N_Tester::test_enqueue_tail (void) +{ + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue_Ex_N<ACE_NULL_SYNCH>, test_enqueue_tail"); + + // Send_Messages creates messages and deletes them when it gets out of scope + Send_Messages messages (max_messages, chain_limit); + if (-1 == messages.create_messages (test_message)) + { + return -1; + } + Receive_Messages r_messages (max_messages); + if (-1 == r_messages.create ()) + { + return -1; + } + + // prepare + int limit = max_messages / chain_limit; + timer->start (); + // Send with just one call + for (int i = 0; i < limit; ++i) + { + if (-1 == this->st_queue_.enqueue_tail (messages.send_block_[i])) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue_tail_n")), + -1); + } + + for (int j = 0, k = 0; j < chain_limit; ++j, ++k) + { + if (this->st_queue_.dequeue_head (r_messages.receive_block_[k]) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + -1); + } + } + } + timer->stop (); + + print_message (message); + + timer->reset (); + + return 0; +} + +int +MQ_Ex_N_Tester::test_enqueue_head (void) +{ + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue_Ex_N<ACE_NULL_SYNCH>, test_enqueue_head"); + + // Send_Messages creates messages and deletes them when it gets out of scope + Send_Messages messages (max_messages, chain_limit); + if (-1 == messages.create_messages (test_message)) + { + return -1; + } + Receive_Messages r_messages (max_messages); + if (-1 == r_messages.create ()) + { + return -1; + } + + // prepare + int i, j, k = 0; + + int limit = max_messages / chain_limit; + timer->start (); + + // Send/receive the messages. + // Send with just one call + for (i = 0; i < limit; ++i) + { + if (-1 == this->st_queue_.enqueue_head (messages.send_block_[i])) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue_tail_n")), + -1); + } + + for (j = 0; j < chain_limit; ++j, ++k) + { + if (this->st_queue_.dequeue_head (r_messages.receive_block_[k]) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + -1); + } + } + } + timer->stop (); + + print_message (message); + + timer->reset (); + + return 0; + +} + +#if defined (ACE_HAS_THREADS) + +static void * +receiver (void *arg) +{ + Queue_Wrapper *queue_wrapper = reinterpret_cast<Queue_Wrapper *> (arg); + int i; + + User_Class **receive_block_p = 0; + ACE_NEW_RETURN (receive_block_p, + User_Class *[max_messages], + (void *) -1); + + for (i = 0; i < max_messages; ++i) + if (queue_wrapper->q_->dequeue_head (receive_block_p[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head")), + 0); + timer->stop (); + + delete [] receive_block_p; + + return 0; +} + +static void * +sender (void *arg) +{ + Queue_Wrapper *queue_wrapper = reinterpret_cast<Queue_Wrapper *> (arg); + int i; + + timer->start (); + + // Send the messages. + for (i = 0; i < max_messages; ++i) + if (queue_wrapper->q_-> + enqueue_tail (queue_wrapper->send_block_[i]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue")), + 0); + return 0; +} + +static int +performance_test (void) +{ + Queue_Wrapper queue_wrapper; + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue_Ex<ACE_SYNCH>"); + int i = 0; + + // Create the messages. Allocate off the heap in case messages is + // large relative to the amount of stack space available. Allocate + // it here instead of in the sender, so that we can delete it after + // the _receiver_ is done. + User_Class **send_block = 0; + ACE_NEW_RETURN (send_block, + User_Class *[max_messages], + -1); + + for (i = 0; i < max_messages; ++i) + ACE_NEW_RETURN (send_block[i], + User_Class (test_message), + -1); + + queue_wrapper.send_block_ = send_block; + + ACE_NEW_RETURN (queue_wrapper.q_, + SYNCH_QUEUE, + -1); + + if (ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) sender, + &queue_wrapper, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning sender thread")), + -1); + + if (ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) receiver, + &queue_wrapper, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning receiver thread")), + -1); + + ACE_Thread_Manager::instance ()->wait (); + print_message (message); + timer->reset (); + + delete queue_wrapper.q_; + queue_wrapper.q_ = 0; + + for (i = 0; i < max_messages; ++i) + delete send_block[i]; + delete [] send_block; + + return 0; +} + +int +MQ_Ex_N_Tester::performance_test (void) +{ + const ACE_TCHAR *message = + ACE_TEXT ("ACE_Message_Queue_Ex_N<ACE_SYNCH>"); + + Send_Messages messages (max_messages, chain_limit); + if (-1 == messages.create_messages (test_message)) + { + return -1; + } + + MQ_Ex_N_Tester_Wrapper tester_wrapper; + tester_wrapper.head_send_block_ = messages.head_send_block_; + tester_wrapper.tester_ = this; + + if (ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) &MQ_Ex_N_Tester::sender, + &tester_wrapper, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning sender thread")), + -1); + + if (ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) &MQ_Ex_N_Tester::receiver, + this, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning receiver thread")), + -1); + + ACE_Thread_Manager::instance ()->wait (); + + print_message (message); + + timer->reset (); + + return 0; +} + +ACE_THR_FUNC_RETURN +MQ_Ex_N_Tester::receiver (void *args) +{ + MQ_Ex_N_Tester *tester = reinterpret_cast<MQ_Ex_N_Tester *> (args); + + User_Class **receive_block_p = 0; + ACE_NEW_RETURN (receive_block_p, + User_Class *[max_messages], + (ACE_THR_FUNC_RETURN) -1); + + int i; + tester_barrier.wait (); + for (i = 0; i < max_messages; ++i) + { + if (tester->mt_queue_.dequeue_head (receive_block_p[i]) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("dequeue_head"))); + return (ACE_THR_FUNC_RETURN) -1; + } + } + timer->stop (); + + delete [] receive_block_p; + + return 0; +} + +ACE_THR_FUNC_RETURN +MQ_Ex_N_Tester::sender (void *args) +{ + MQ_Ex_N_Tester_Wrapper *tester_wrapper = + reinterpret_cast<MQ_Ex_N_Tester_Wrapper *> (args); + MQ_Ex_N_Tester *tester = tester_wrapper->tester_; + + Send_Messages messages (max_messages, chain_limit); + if (-1 == messages.create_messages (test_message)) + { + return (ACE_THR_FUNC_RETURN) -1; + } + int limit = max_messages / chain_limit; + tester_barrier.wait (); + timer->start (); + // Send/receive the messages. + timer->start (); + // Send with just one call + for (int i = 0; i < limit; ++i) + { + if (-1 == tester->mt_queue_.enqueue_tail (messages.send_block_[i])) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("enqueue_tail_n"))); + return (ACE_THR_FUNC_RETURN) -1; + } + } + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int basic_queue_test (ACE_Message_Queue_Ex<User_Class, ACE_SYNCH>& q) +{ + int status = 0; + if (!q.is_empty ()) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("New queue is not empty!\n"))); + status = 1; + } + else + { + User_Class *b; + ACE_Time_Value tv (ACE_OS::gettimeofday ()); // Now + if (q.dequeue_head (b, &tv) != -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Dequeued from empty queue!\n"))); + status = 1; + } + else if (errno != EWOULDBLOCK) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Dequeue timeout should be EWOULDBLOCK, got"))); + status = 1; + } + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Timed dequeue test: OK\n"))); + status = 0; // All is well + } + } + + return status; +} + +int queue_priority_test (ACE_Message_Queue_Ex<User_Class, ACE_SYNCH>& q) +{ + int status = 0; + if (!q.is_empty ()) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Prio test queue not empty\n")), 1); + + // Set up a few objects with names for how they should come out of the queue. + ACE_Auto_Basic_Ptr<User_Class> b1, b2, b3, b4; + b1.reset (new User_Class ("first")); + b2.reset (new User_Class ("second")); + b3.reset (new User_Class ("third")); + b4.reset (new User_Class ("fourth")); + unsigned long prio = + ACE_Message_Queue_Ex<User_Class, ACE_SYNCH>::DEFAULT_PRIORITY; + + prio += 1; + if (-1 == q.enqueue_prio (b2.get (), 0, prio)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("b2")), 1); + if (-1 == q.enqueue_prio (b3.get (), 0, prio)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("b3")), 1); + prio -= 1; + if (-1 == q.enqueue_prio (b4.get (), 0, prio)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("b4")), 1); + prio += 5; + if (-1 == q.enqueue_prio (b1.get (), 0, prio)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("b1")), 1); + + User_Class *b = 0; + if (q.dequeue_head (b) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("dequeue 1"))); + ++status; + } + else + { + if (ACE_OS::strcmp (b->message (), "first") != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("First dequeued was %C\n"), + b->message ())); + ++status; + } + } + if (q.dequeue_head (b) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("dequeue 2"))); + ++status; + } + else + { + if (ACE_OS::strcmp (b->message (), "second") != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Second dequeued was %C\n"), + b->message ())); + ++status; + } + } + if (q.dequeue_head (b) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("dequeue 3"))); + ++status; + } + else + { + if (ACE_OS::strcmp (b->message (), "third") != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Third dequeued was %C\n"), + b->message ())); + ++status; + } + } + if (q.dequeue_head (b) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("dequeue 4"))); + ++status; + } + else + { + if (ACE_OS::strcmp (b->message (), "fourth") != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Fourth dequeued was %C\n"), + b->message ())); + ++status; + } + } + + if (status == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Priority queueing test: OK\n"))); + return status; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Message_Queue_Test_Ex")); + + if (argc == 2) + if (! ACE_OS::strcmp (argv[1], ACE_TEXT ("-?"))) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s/n"), + usage)); + else + max_messages = ACE_OS::atoi (argv[1]); + + int status = 0; + + // Be sure that the a timed out get sets the error code properly. + ACE_Message_Queue_Ex<User_Class, ACE_SYNCH> q1; + ACE_Message_Queue_Ex_N<User_Class, ACE_SYNCH> q2; + if (0 != basic_queue_test (q1) || + 0 != basic_queue_test (q2)) + { + ++status; + } + + // Check priority operations. + if (0 != queue_priority_test (q1)) + { + ++status; + } + + ACE_NEW_RETURN (timer, + ACE_High_Res_Timer, + -1); + + status += single_thread_performance_test (); + +#if defined (ACE_HAS_THREADS) + status += performance_test (); +#endif /* ACE_HAS_THREADS */ + + { + MQ_Ex_N_Tester ex_n_tester; + status += ex_n_tester.single_thread_performance_test (); +#if defined (ACE_HAS_THREADS) + status += ex_n_tester.performance_test (); +#endif /* ACE_HAS_THREADS */ + } + + if (status != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test failed"))); + delete timer; + timer = 0; + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/Message_Queue_Test_Ex.h b/ACE/tests/Message_Queue_Test_Ex.h new file mode 100644 index 00000000000..38d076ac729 --- /dev/null +++ b/ACE/tests/Message_Queue_Test_Ex.h @@ -0,0 +1,89 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Message_Queue_Test_Ex.h + * + * $Id$ + * + * Define class needed for generating templates. IBM C++ requires this + * to be in its own file for auto template instantiation. + * + * @author Michael Vitlo <mvitalo@sprynet.com> + * @author Irfan Pyarali <irfan@cs.wustl.edu> + * @author David L. Levine <levine@cs.wustl.edu> + * @author Guy Peleg <guy.peleg@amdocs.com> + */ +//============================================================================= + +#ifndef ACE_TESTS_MESSAGE_QUEUE_TEST_EX_H +#define ACE_TESTS_MESSAGE_QUEUE_TEST_EX_H + +#include "ace/OS_NS_string.h" + +// User-defined class used for queue data. +class User_Class +{ +public: + User_Class (const char inputMsg[]) + : message_ (0), + next_(0) + { + ACE_NEW (this->message_, char[ACE_OS::strlen (inputMsg) + 1]); + ACE_OS::strcpy (this->message_, inputMsg); + } + + ~User_Class (void) { delete [] this->message_; } + + const char *message (void) const + { + return this->message_; + } + + // This is for checking the ACE_Message_Queue_Ex_N + User_Class *next () const + { + return this->next_; + } + + void next (User_Class *uc) + { + this->next_ = uc; + } + +private: + char *message_; + User_Class *next_; +}; + +// The main tests for the ACE_Message_Queue_Ex_N +struct Receive_Messages; + +class MQ_Ex_N_Tester +{ +public: + int single_thread_performance_test (void); + +#if defined (ACE_HAS_THREADS) + int performance_test (void); + + /// Sender runs with an autonomous thread + static ACE_THR_FUNC_RETURN sender (void *); + + /// Receiver runs with an autonomous thread + static ACE_THR_FUNC_RETURN receiver (void *); + + /// Multi threaded tests use this queue + ACE_Message_Queue_Ex_N<User_Class, ACE_MT_SYNCH> mt_queue_; +#endif /* ACE_HAS_THREADS */ + + /// Single threaded tests use this queue + ACE_Message_Queue_Ex_N<User_Class, ACE_NULL_SYNCH> st_queue_; + +private: + /// Helper methods + int test_enqueue_head (void); + int test_enqueue_tail (void); +}; + +#endif /* ACE_TESTS_MESSAGE_QUEUE_TEST_EX_H */ diff --git a/ACE/tests/Multicast_Test.cpp b/ACE/tests/Multicast_Test.cpp new file mode 100644 index 00000000000..794e7ff0d60 --- /dev/null +++ b/ACE/tests/Multicast_Test.cpp @@ -0,0 +1,963 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program tests ACE_SOCK_Dgram_Mcast class. +// It specifically tests subscribing to multiple groups on the same socket +// on one or more physical interfaces (if available). +// +// The test can be run as a producer, consumer, or both producer/consumer +// (default). The test requires at least two (2) multicast groups which can +// be configured as command line options. The consumer subscribes to a +// single group per instance and an additional instance tries to subscribe +// to all groups on a single socket (if the ACE_SOCK_Dgram_Mcast instance +// bind()'s the first address to the socket, additional joins will fail). +// The producer iterates through the list of group addresses and sends a +// single message containing the destination address and port to each one. +// It also sends messages to five (5) additional groups and a message to an +// additional port for each group in order to produce a bit of "noise" in +// order to help validate how well the multicast filtering works on a +// particular platform. +// +// The list of destination groups start at 239.255.0.1 (default) and +// increment by 1 up to 5 (default) groups. Both of these values, as well +// as others, can be overridden via command-line options. Use the -? +// option to display the usage message... +// +// = AUTHOR +// Don Hinton <dhinton@dresystems.com> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Get_Opt.h" +#include "ace/Vector_T.h" +#include "ace/SOCK_Dgram_Mcast.h" +#include "ace/ACE.h" +#include "ace/Reactor.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_strings.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Task.h" +#include "ace/Atomic_Op.h" +#include "ace/SString.h" +#include "ace/Signal.h" +#include "ace/Min_Max.h" + +ACE_RCSID(tests, Multicast_Test, "$Id$") + +#if defined (ACE_HAS_IP_MULTICAST) && defined (ACE_HAS_THREADS) + +/* + * The 'finished' flag is used to break out of an infninite loop in the + * task::svc () method. The 'handler' will set the flag in respose to + * SIGINT (CTRL-C). + */ +static sig_atomic_t finished = 0; +extern "C" void handler (int) +{ + finished = 1; +} + +static const int MCT_ITERATIONS = 10; +static const int MCT_GROUPS = 5; +static const int MCT_MIN_GROUPS = 2; + +static const char MCT_START_GROUP[] = "239.255.0.1"; +static const int MCT_START_PORT = 16000; + +static const size_t MAX_STRING_SIZE = 200; + +int advance_addr (ACE_INET_Addr &addr); + +// Keep track of errors so we can report them on exit. +static sig_atomic_t error = 0; + +/* + * MCast_Config holds configuration data for this test. + */ +class MCT_Config +{ +public: + + enum + { + PRODUCER = 1, + CONSUMER = 2, + BOTH = PRODUCER | CONSUMER + }; + + MCT_Config (void) + : group_start_ (MCT_START_PORT, MCT_START_GROUP), + groups_ (0), + debug_ (0), + role_ (BOTH), + sdm_opts_ (ACE_SOCK_Dgram_Mcast::DEFOPTS), + iterations_ (MCT_ITERATIONS), + ttl_ (1), + wait_ (2) + { + if (IP_MAX_MEMBERSHIPS == 0) + this->groups_ = MCT_GROUPS; + else + this->groups_ = ACE_MIN (IP_MAX_MEMBERSHIPS, MCT_GROUPS); + } + ~MCT_Config (void) + {} + + //FUZZ: disable check_for_lack_ACE_OS + int open (int argc, ACE_TCHAR *argv[]); + //FUZZ: enable check_for_lack_ACE_OS + + int debug (void) const { return this->debug_;} + void dump (void) const; + int groups (void) const { return this->groups_;} + const ACE_INET_Addr group_start (void) const { return this->group_start_;} + u_long role (void) const { return this->role_;} + int iterations (void) const { return this->iterations_;} + int ttl (void) const { return this->ttl_;} + + //FUZZ: disable check_for_lack_ACE_OS + int wait (void) const { return this->wait_;} + //FUZZ: enable check_for_lack_ACE_OS + + ACE_SOCK_Dgram_Mcast::options options (void) const + { + return static_cast<ACE_SOCK_Dgram_Mcast::options> (this->sdm_opts_); + } + +private: + // Starting group address. (only IPv4 capable right now...) + ACE_INET_Addr group_start_; + + // Number of groups we will try to use in the test. + int groups_; + + // Debug flag. + int debug_; + + // Role, i.e., PRODUCER, CONSUMER, BOTH: defaults to BOTH + u_long role_; + + // ACE_SOCK_Dgram_Mcast ctor options + u_long sdm_opts_; + + // Producer iterations + int iterations_; + + // TTL, time to live, for use over routers. + int ttl_; + + // Time to wait on CONSUMER threads to end before killing test. + int wait_; +}; + +int +MCT_Config::open (int argc, ACE_TCHAR *argv[]) +{ + int retval = 0; + int help = 0; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT (":?"), 1, 1); + //FUZZ: enable check_for_lack_ACE_OS + + if (getopt.long_option (ACE_TEXT ("GroupStart"), + 'g', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add GroupStart option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("Groups"), + 'n', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Groups option.\n")), 1); + + if (getopt.long_option (ACE_TEXT ("Debug"), + 'd', + ACE_Get_Opt::NO_ARG) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Debug option.\n")), 1); + + if (getopt.long_option (ACE_TEXT ("Role"), + 'r', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Role option.\n")), 1); + + if (getopt.long_option (ACE_TEXT ("SDM_options"), + 'm', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Multicast_Options option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("Iterations"), + 'i', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add iterations option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("TTL"), + 't', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add TTL option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("Wait"), + 'w', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add wait option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("help"), + 'h', + ACE_Get_Opt::NO_ARG) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add help option.\n")), + 1); + + //FUZZ: disable check_for_lack_ACE_OS + // Now, let's parse it... + int c = 0; + while ((c = getopt ()) != EOF) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 0: + // Long Option. This should never happen. + retval = -1; + break; + case 'g': + { + // @todo validate all these, i.e., must be within range + // 224.255.0.0 to 238.255.255.255, but we only allow the + // administrative "site local" range, 239.255.0.0 to + // 239.255.255.255. + ACE_TCHAR *group = getopt.opt_arg (); + if (this->group_start_.set (group) != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Bad group address:%s\n"), + group)); + } + } + break; + case 'i': + this->iterations_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'n': + { + int n = ACE_OS::atoi (getopt.opt_arg ()); + // I'm assuming 0 means unlimited, so just use whatever the + // user provides. Seems to work okay on Solaris 5.8. + if (IP_MAX_MEMBERSHIPS == 0) + this->groups_ = n; + else + this->groups_ = ACE_MIN (ACE_MAX (n, MCT_MIN_GROUPS), + IP_MAX_MEMBERSHIPS); + break; + } + case 'd': + this->debug_ = 1; + break; + case 'r': + { + ACE_TCHAR *c = getopt.opt_arg (); + if (ACE_OS::strcasecmp (c, ACE_TEXT ("CONSUMER")) == 0) + this->role_ = CONSUMER; + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("PRODUCER")) == 0) + this->role_ = PRODUCER; + else + { + help = 1; + retval = -1; + } + } + break; + case 'm': + { + //@todo add back OPT_BINDADDR_NO... + ACE_TCHAR *c = getopt.opt_arg (); + if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_BINDADDR_YES")) == 0) + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_BINDADDR_NO")) == 0) + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("DEFOPT_BINDADDR")) == 0) + { + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES); + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::DEFOPT_BINDADDR); + } + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_NULLIFACE_ALL")) == 0) + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_NULLIFACE_ALL); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_NULLIFACE_ONE")) == 0) + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_NULLIFACE_ALL); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("DEFOPT_NULLIFACE")) == 0) + { + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_NULLIFACE_ALL); + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::DEFOPT_NULLIFACE); + } + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("DEFOPTS")) == 0) + this->sdm_opts_ = ACE_SOCK_Dgram_Mcast::DEFOPTS; + else + { + help = 1; + retval = -1; + } + } + break; + case 't': + this->ttl_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'w': + this->wait_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + case ':': + // This means an option requiring an argument didn't have one. + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" Option '%c' requires an argument but ") + ACE_TEXT ("none was supplied\n"), + getopt.opt_opt ())); + help = 1; + retval = -1; + break; + case '?': + case 'h': + default: + if (ACE_OS::strcmp (argv[getopt.opt_ind () - 1], ACE_TEXT ("-?")) != 0 + && getopt.opt_opt () != 'h') + // Don't allow unknown options. + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" Found an unknown option (%c) ") + ACE_TEXT ("we couldn't handle.\n"), + getopt.opt_opt ())); + // getopt.last_option ())); //readd with "%s" when + // last_option() is available. + help = 1; + retval = -1; + break; + } + } + + if (retval == -1) + { + if (help) + // print usage here + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s [options]\n") + ACE_TEXT ("Options:\n") + ACE_TEXT (" -g {STRING} --GroupStart={STRING} ") + ACE_TEXT ("starting multicast group address\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=239.255.0.1:16000)\n") + ACE_TEXT (" -n {#} --Groups={#} ") + ACE_TEXT ("number of groups (default=5)\n") + ACE_TEXT (" -d --Debug ") + ACE_TEXT ("debug flag (default=off)\n") + ACE_TEXT (" -r {STRING} --Role={STRING} ") + ACE_TEXT ("role {PRODUCER|CONSUMER|BOTH}\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=BOTH)\n") + ACE_TEXT (" -m {STRING} --SDM_options={STRING} ") + ACE_TEXT ("ACE_SOCK_Dgram_Mcast ctor options\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=DEFOPTS)\n") + ACE_TEXT (" -i {#} --Iterations={#} ") + ACE_TEXT ("number of iterations (default=100)\n") + ACE_TEXT (" -t {#} --TTL={#} ") + ACE_TEXT ("time to live (default=1)\n") + ACE_TEXT (" -w {#} --Wait={#} ") + ACE_TEXT ("number of seconds to wait on CONSUMER\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=2)\n") + ACE_TEXT (" -h/? --help ") + ACE_TEXT ("show this message\n"), + argv[0])); + + return -1; + } + + return 0; +} + +void +MCT_Config::dump (void) const +{ + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" Dumping MCT_Config\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tIP_MAX_MEMBERSHIPS = %d\n"), + IP_MAX_MEMBERSHIPS)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tgroups_ = %d\n"), + this->groups_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\trole_ = %s\n"), + (ACE_BIT_ENABLED (this->role_, PRODUCER) + && ACE_BIT_ENABLED (this->role_, CONSUMER)) + ? ACE_TEXT ("PRODUCER/CONSUMER") + : ACE_BIT_ENABLED (this->role_, PRODUCER) + ? ACE_TEXT ("PRODUCER") + : ACE_TEXT ("CONSUMER"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tsdm_options_ = %d\n"), + this->sdm_opts_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\titerations_ = %d\n"), + this->iterations_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tttl_ = %d\n"), + this->ttl_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\twait_ = %d\n"), + this->wait_)); + // Note that this call to get_host_addr is the non-reentrant + // version, but it's okay for us. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tgroups_start_ = %s:%d\n"), + this->group_start_.get_host_addr (), + this->group_start_.get_port_number ())); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +/******************************************************************************/ + +class MCT_Event_Handler : public ACE_Event_Handler +{ +public: + MCT_Event_Handler (ACE_SOCK_Dgram_Mcast::options options + = ACE_SOCK_Dgram_Mcast::DEFOPTS); + virtual ~MCT_Event_Handler (void); + + int join (const ACE_INET_Addr &mcast_addr, + int reuse_addr = 1, + const ACE_TCHAR *net_if = 0); + int leave (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if = 0); + + // = Event Handler hooks. + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask close_mask); + + virtual ACE_HANDLE get_handle (void) const; + +protected: + ACE_SOCK_Dgram_Mcast *mcast (void); + int find (const char *buf); + +private: + ACE_SOCK_Dgram_Mcast mcast_; + + // List of groups we've joined + ACE_Vector<ACE_CString*> address_vec_; + + // Flag used to set the 'finished' flag when the last event handler + // gets removed from the reactor. + static ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> active_handlers_; +}; + +ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> MCT_Event_Handler::active_handlers_ = 0; + +MCT_Event_Handler::MCT_Event_Handler (ACE_SOCK_Dgram_Mcast::options options) + : mcast_ (options) +{ + // Increment the number of active handlers in the reactor. Note this isn't + // really correct, but it should work for our simple example. + ++MCT_Event_Handler::active_handlers_; +} + +MCT_Event_Handler::~MCT_Event_Handler (void) +{ + size_t size = this->address_vec_.size (); + for (size_t i = 0; i < size; ++i) + { + delete this->address_vec_[i]; + this->address_vec_[i] = 0; + } + mcast_.close (); +} + + +ACE_SOCK_Dgram_Mcast * +MCT_Event_Handler::mcast (void) +{ + return &this->mcast_; +} + +int +MCT_Event_Handler::find (const char *buf) +{ + size_t size = this->address_vec_.size (); + size_t i; + for (i = 0; i < size; ++i) + { + if (ACE_OS::strcasecmp (buf, this->address_vec_[i]->c_str ()) == 0) + return 0; + } + + // Not found, so output message we received along with a list of groups + // we've joined for debugging. + ACE_CString local; + for (i = 0; i < size; ++i) + { + local += "\t"; + local += this->address_vec_[i]->c_str (); + local += "\n"; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%C not in:\n%C"), + buf, local.c_str ())); + + return -1; +} + + +int +MCT_Event_Handler::join (const ACE_INET_Addr &mcast_addr, + int reuse_addr, + const ACE_TCHAR *net_if) +{ + char buf[MAX_STRING_SIZE]; + ACE_OS::sprintf (buf, "%s/%d", + mcast_addr.get_host_addr (), + mcast_addr.get_port_number ()); + + if (this->mcast_.join (mcast_addr, reuse_addr, net_if) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Event_Handler::join %C %p\n"), + buf, + ACE_TEXT ("failed")), + -1); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Joined %C\n"), buf)); + + ACE_CString *str; + ACE_NEW_RETURN (str, ACE_CString (buf), -1); + this->address_vec_.push_back (str); + return 0; +} + +int +MCT_Event_Handler::leave (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if) +{ + if (this->mcast_.leave (mcast_addr, net_if) == 0) + { + char buf[MAX_STRING_SIZE]; + size_t size = this->address_vec_.size (); + for (size_t i = 0; i < size; ++i) + { + ACE_OS::sprintf (buf, "%s/%d", + mcast_addr.get_host_addr (), + mcast_addr.get_port_number ()); + if (ACE_OS::strcasecmp (buf, this->address_vec_[i]->c_str ()) == 0) + { + this->address_vec_[i]->set (""); + break; + } + } + return 0; + } + return -1; +} + +int +MCT_Event_Handler::handle_input (ACE_HANDLE /*handle*/) +{ + char buf[MAX_STRING_SIZE]; + ACE_OS::memset (buf, 0, sizeof buf); + ACE_INET_Addr addr; + + if (this->mcast ()->recv (buf, sizeof buf, addr) == -1) + { + ++error; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Event_Handler::handle_input - ") + ACE_TEXT ("calling recv\n")), -1); + } + + // Zero length buffer means we are done. + if (ACE_OS::strlen (buf) == 0) + return -1; + else if (this->find (buf) == -1) + { + ++error; + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("MCT_Event_Handler::handle_input - ") + ACE_TEXT ("Received dgram for a group we didn't join ") + ACE_TEXT ("(%s) \n"), + buf)); + } + return 0; +} + +int +MCT_Event_Handler::handle_close (ACE_HANDLE /*fd*/, + ACE_Reactor_Mask /*close_mask*/) +{ + // If this is the last handler, use the finished flag to signal + // the task to exit. + if (--MCT_Event_Handler::active_handlers_ == 0) + finished = 1; + + // The DONT_CALL flag keeps the reactor from calling handle_close () + // again, since we commit suicide below. + this->reactor ()->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); + this->reactor (0); + delete this; + return 0; +} + +ACE_HANDLE +MCT_Event_Handler::get_handle (void) const +{ + return this->mcast_.get_handle (); +} + +/******************************************************************************/ + +/* + * Our MCT_Task object will be an Active Object if we are running the Consumer + * side of the test. open() calls active() which creates a thread and calls + * the svc() method that calls runs the reactor event loop. + */ +class MCT_Task : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + MCT_Task (const MCT_Config &config, + ACE_Reactor *reactor = ACE_Reactor::instance ()); + ~MCT_Task (void); + + //FUZZ: disable check_for_lack_ACE_OS + // = Task hooks. + virtual int open (void *args = 0); + //FUZZ: enable check_for_lack_ACE_OS + + virtual int svc (void); + +private: + const MCT_Config &config_; + int iterations_; +}; + +MCT_Task::MCT_Task (const MCT_Config &config, + ACE_Reactor *reactor) + : config_ (config) +{ + this->reactor (reactor); +} + +MCT_Task::~MCT_Task (void) +{} + +int +MCT_Task::open (void *) +{ + MCT_Event_Handler *handler; + + ACE_INET_Addr addr = this->config_.group_start (); + int groups = this->config_.groups (); + for (int i = 0; i < groups; ++i) + { + ACE_NEW_RETURN (handler, + MCT_Event_Handler (this->config_.options ()), -1); + // We subscribe to all groups for the first one and one each for + // all the others. + if (i == 0) + { + // go ahead and hide the other one since we want our own. + ACE_INET_Addr addr = this->config_.group_start (); + for (int j = 0; j < groups; ++j) + { + // If OPT_BINDADDR_YES is set, this will fail after the first + // join, so just break and keep on going, otherwise it's a + // real error. + if (j > 0 + && ACE_BIT_ENABLED (ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES, + this->config_.options ())) + break; + + if (handler->join (addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Task::open - join error\n")), + -1); + advance_addr (addr); + } + } + else + { + if (handler->join (addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Task::open - join error\n")), + -1); + } + + advance_addr (addr); + + if (this->reactor ()->register_handler (handler, READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Task::open - cannot register ") + ACE_TEXT ("handler\n")), + -1); + } + + if (this->activate (THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("MCT_TASK:open - activate failed")), + -1); + return 0; +} + +int +MCT_Task::svc (void) +{ + // make sure this thread owns the reactor or handle_events () won't do + // anything. + this->reactor ()->owner (ACE_Thread::self ()); + + // loop and call handle_events... + while (!finished) + this->reactor ()->handle_events (); + + return 0; +} + +/******************************************************************************/ + +int send_dgram (ACE_SOCK_Dgram &socket, ACE_INET_Addr addr, int done = 0) +{ + + // Send each message twice, once to the right port, and once to the "wrong" + // port. This helps generate noise and lets us see if port filtering is + // working properly. + const char *address = addr.get_host_addr (); + int port = addr.get_port_number (); + + for (int i = 0; i < 2; ++i) + { + char buf[MAX_STRING_SIZE]; + if (done) + buf[0] = 0; + else + ACE_OS::sprintf (buf, "%s/%d", address, port); + //ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("sending (%s)\n"), buf)); + if (socket.send (buf, ACE_OS::strlen (buf),addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("send_dgram - error calling send on ") + ACE_TEXT ("ACE_SOCK_Dgram.")), -1); + addr.set_port_number (++port); + } + return 0; +} + +int producer (MCT_Config &config) +{ + int retval = 0; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Starting producer...\n"))); + ACE_SOCK_Dgram socket (ACE_sap_any_cast (ACE_INET_Addr &), PF_INET); + //FUZZ: enable check_for_lack_ACE_OS + + // Note that is is IPv4 specific and needs to be changed once + // + if (config.ttl () > 1) + { + int ttl = config.ttl (); + if (socket.set_option (IPPROTO_IP, + IP_MULTICAST_TTL, + (void*) &ttl, + sizeof ttl) != 0) + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("could net set socket option IP_MULTICAST_TTL ") + ACE_TEXT ("= %d\n"), + ttl)); + else + ACE_DEBUG ((LM_INFO, ACE_TEXT ("set IP_MULTICAST_TTL = %d\n"), ttl)); + } + + int iterations = config.iterations (); + // we add an extra 5 groups for noise. + int groups = config.groups () + 5; + for (int i = 0; (i < iterations || iterations == 0) && !finished; ++i) + { + ACE_INET_Addr addr = config.group_start (); + for (int j = 0; j < groups && !finished; ++j) + { + if ((retval += send_dgram (socket, addr, + ((i + 1) == iterations))) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Calling send_dgram.\n"))); + if ((retval += advance_addr (addr)) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Calling advance_addr.\n"))); + } + // Give the task thread a chance to run. + ACE_Thread::yield (); + } + socket.close (); + return retval; +} + +/* + * Advance the address by 1, e.g., 239.255.0.1 => 239.255.0.2 + * Note that the algorithm is somewhat simplistic, but sufficient for our + * purpose. + */ +int advance_addr (ACE_INET_Addr &addr) +{ + int a, b, c, d; + ::sscanf (addr.get_host_addr (), "%d.%d.%d.%d", &a, &b, &c, &d); + if (d < 255) + ++d; + else if (c < 255) + { + d = 1; + ++c; + } + else if (b < 255) + { + d = 1; + c = 0; + ++b; + } + else if (a < 239) + { + d = 1; + c = 0; + b = 0; + ++a; + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("advance_addr - Cannot advance multicast ") + ACE_TEXT ("group address past %s\n"), + addr.get_host_addr ()), + -1); + + ACE_TCHAR buf[MAX_STRING_SIZE]; + ACE_OS::sprintf (buf, ACE_TEXT ("%d.%d.%d.%d:%d"), + a, b, c, d, addr.get_port_number ()); + addr.set (buf); + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + int retval = 0; + MCT_Config config; + retval = config.open (argc, argv); + if (retval != 0) + return 1; + + const ACE_TCHAR *temp = ACE_TEXT ("Multicast_Test"); + ACE_TString test = temp; + + u_long role = config.role (); + if (ACE_BIT_DISABLED (role, MCT_Config::PRODUCER) + || ACE_BIT_DISABLED (role, MCT_Config::CONSUMER)) + { + if (ACE_BIT_ENABLED (role, MCT_Config::PRODUCER)) + test += ACE_TEXT ("-PRODUCER"); + else + test += ACE_TEXT ("-CONSUMER"); + } + + // Start test only if options are valid. + ACE_START_TEST (test.c_str ()); + + // Register a signal handler to close down application gracefully. + ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); + + // Dump the configuration info to the log if caller passed debug option. + if (config.debug ()) + config.dump (); + + ACE_Reactor *reactor = ACE_Reactor::instance (); + + MCT_Task *task = new MCT_Task (config, reactor); + + if (ACE_BIT_ENABLED (role, MCT_Config::CONSUMER)) + { + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Starting consumer...\n"))); + // Open makes it an active object. + retval += task->open (); + } + + // now produce the datagrams... + if (ACE_BIT_ENABLED (role, MCT_Config::PRODUCER)) + retval += producer (config); + + if (ACE_BIT_ENABLED (role, MCT_Config::CONSUMER)) + { + // and wait for everything to finish + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("start waiting for consumer to finish...\n"))); + // Wait for the threads to exit. + // But, wait for a limited time since we could hang if the last udp + // message isn't received. + ACE_Time_Value max_wait ( config.wait ()/* seconds */); + ACE_Time_Value wait_time (ACE_OS::gettimeofday () + max_wait); + ACE_Time_Value *ptime = ACE_BIT_ENABLED (role, MCT_Config::PRODUCER) + ? &wait_time : 0; + if (ACE_Thread_Manager::instance ()->wait (ptime) == -1) + { + // We will no longer wait for this thread, so we must + // force it to exit otherwise the thread will be referencing + // deleted memory. + finished = 1; + reactor->end_reactor_event_loop (); + + if (errno == ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("maximum wait time of %d msec exceeded\n"), + max_wait.msec ())); + else + ACE_OS::perror (ACE_TEXT ("wait")); + + ++error; + + // This should exit now that we ended the reactor loop. + task->wait (); + } + } + + delete task; + ACE_END_TEST; + return (retval == 0 && error == 0) ? 0 : 1; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Multicast_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("This test must be run on a platform ") + ACE_TEXT ("that support IP multicast.\n"))); + + ACE_END_TEST; + return 1; +} +#endif /* ACE_HAS_IP_MULTICAST && ACE_HAS_THREADS */ diff --git a/ACE/tests/Multicast_Test_IPV6.cpp b/ACE/tests/Multicast_Test_IPV6.cpp new file mode 100644 index 00000000000..337d3a43b5c --- /dev/null +++ b/ACE/tests/Multicast_Test_IPV6.cpp @@ -0,0 +1,1039 @@ +// $Id$ +// ============================================================================ +//FUZZ: disable check_for_lack_ACE_OS +/** + * @file Multicast_Test_IPV6.cpp + * + * @brief This program tests ACE_SOCK_Dgram_Mcast class. + * + * It specifically tests subscribing to multiple groups on the same + * socket on one or more physical interfaces (if available). + * + * The test can be run as a producer, consumer, or both + * producer/consumer (default). The test requires at least two (2) + * multicast groups which can be configured as command line options. + * The consumer subscribes to a single group per instance and an + * additional instance tries to subscribe to all groups on a single + * socket (if the ACE_SOCK_Dgram_Mcast instance bind()'s the first + * address to the socket, additional joins will fail). The producer + * iterates through the list of group addresses and sends a single + * message containing the destination address and port to each one. It + * also sends messages to five (5) additional groups and a message to + * an additional port for each group in order to produce a bit of + * "noise" in order to help validate how well the multicast filtering + * works on a particular platform. + * + * The list of destination groups start at ff01::1 (default) and + * increment by 1 up to 5 (default) groups. Both of these values, as + * well as others, can be overridden via command-line options. Use + * the -? option to display the usage message... + * + * @author Don Hinton <dhinton@dresystems.com> + * Brian Buesker <bbuesker@qualcomm.com> + */ +// ============================================================================ +//FUZZ: enable check_for_lack_ACE_OS + +#include "tests/test_config.h" +#include "ace/Get_Opt.h" +#include "ace/Vector_T.h" +#include "ace/SOCK_Dgram_Mcast.h" +#include "ace/ACE.h" +#include "ace/Reactor.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_strings.h" +#include "ace/Task.h" +#include "ace/Atomic_Op.h" +#include "ace/SString.h" +#include "ace/Signal.h" +#include "ace/Min_Max.h" + +#if defined (ACE_HAS_IP_MULTICAST) && defined (ACE_HAS_THREADS) + +/* + * The 'finished' flag is used to break out of an infinite loop in the + * task::svc () method. The 'handler' will set the flag in respose to + * SIGINT (CTRL-C). + */ +static sig_atomic_t finished = 0; +extern "C" void handler (int) +{ + finished = 1; +} + +static const int MCT_ITERATIONS = 10; +static const int MCT_GROUPS = 5; +static const int MCT_MIN_GROUPS = 2; + +#if defined (ACE_HAS_IPV6) +static const char MCT_START_GROUP[] = "ff01::1"; +#else +// an IPv4 address that will ensure an error message is not printed when +// IPv6 is not enabled +static const char MCT_START_GROUP[] = "239.255.0.1"; +#endif /* ACE_HAS_IPV6 */ +static const int MCT_START_PORT = 16000; + +static const size_t MAX_STRING_SIZE = 200; + +int advance_addr (ACE_INET_Addr &addr); + +// Keep track of errors so we can report them on exit. +static sig_atomic_t error = 0; + +/* + * MCast_Config holds configuration data for this test. + */ +class MCT_Config +{ +public: + + enum + { + PRODUCER = 1, + CONSUMER = 2, + BOTH = PRODUCER | CONSUMER + }; + + MCT_Config (void) + : group_start_ (MCT_START_PORT, MCT_START_GROUP), + groups_ (0), + debug_ (0), + role_ (BOTH), + sdm_opts_ (ACE_SOCK_Dgram_Mcast::DEFOPTS), + iterations_ (MCT_ITERATIONS), + ttl_ (1), + wait_ (2) + { + if (IP_MAX_MEMBERSHIPS == 0) + this->groups_ = MCT_GROUPS; + else + this->groups_ = ACE_MIN (IP_MAX_MEMBERSHIPS, MCT_GROUPS); + } + + ~MCT_Config (void) + {} + + //FUZZ: disable check_for_lack_ACE_OS + int open (int argc, ACE_TCHAR *argv[]); + //FUZZ: enable check_for_lack_ACE_OS + + int debug (void) const { return this->debug_;} + void dump (void) const; + int groups (void) const { return this->groups_;} + const ACE_INET_Addr group_start (void) const { return this->group_start_;} + u_long role (void) const { return this->role_;} + int iterations (void) const { return this->iterations_;} + int ttl (void) const { return this->ttl_;} + + //FUZZ: disable check_for_lack_ACE_OS + int wait (void) const { return this->wait_;} + //FUZZ: enable check_for_lack_ACE_OS + + ACE_SOCK_Dgram_Mcast::options options (void) const + { + return static_cast<ACE_SOCK_Dgram_Mcast::options> (this->sdm_opts_); + } + + int set_group (int port, const char *group); + +private: + // Starting group address. + ACE_INET_Addr group_start_; + + // Number of groups we will try to use in the test. + int groups_; + + // Debug flag. + int debug_; + + // Role, i.e., PRODUCER, CONSUMER, BOTH: defaults to BOTH + u_long role_; + + // ACE_SOCK_Dgram_Mcast ctor options + u_long sdm_opts_; + + // Producer iterations + int iterations_; + + // TTL, time to live, for use over routers. + int ttl_; + + // Time to wait on CONSUMER threads to end before killing test. + int wait_; +}; + +int +MCT_Config::open (int argc, ACE_TCHAR *argv[]) +{ + int retval = 0; + int help = 0; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT (":?"), 1, 1); + //FUZZ: enable check_for_lack_ACE_OS + + if (getopt.long_option (ACE_TEXT ("GroupStart"), + 'g', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add GroupStart option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("Groups"), + 'n', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Groups option.\n")), 1); + + if (getopt.long_option (ACE_TEXT ("Debug"), + 'd', + ACE_Get_Opt::NO_ARG) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Debug option.\n")), 1); + + if (getopt.long_option (ACE_TEXT ("Role"), + 'r', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Role option.\n")), 1); + + if (getopt.long_option (ACE_TEXT ("SDM_options"), + 'm', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add Multicast_Options option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("Iterations"), + 'i', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add iterations option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("TTL"), + 't', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add TTL option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("Wait"), + 'w', + ACE_Get_Opt::ARG_REQUIRED) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add wait option.\n")), + 1); + + if (getopt.long_option (ACE_TEXT ("help"), + 'h', + ACE_Get_Opt::NO_ARG) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" Unable to add help option.\n")), + 1); + + // Now, let's parse it... + int c = 0; + + //FUZZ: disable check_for_lack_ACE_OS + while ((c = getopt ()) != EOF) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 0: + // Long Option. This should never happen. + retval = -1; + break; + case 'g': + { + // @todo validate all these, i.e., must be within range + // 224.255.0.0 to 238.255.255.255, but we only allow the + // administrative "site local" range, 239.255.0.0 to + // 239.255.255.255. + ACE_TCHAR *group = getopt.opt_arg (); + if (this->group_start_.set (group) != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Bad group address:%s\n"), + group)); + } + } + break; + case 'i': + this->iterations_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'n': + { + int n = ACE_OS::atoi (getopt.opt_arg ()); + // I'm assuming 0 means unlimited, so just use whatever the + // user provides. Seems to work okay on Solaris 5.8. + if (IP_MAX_MEMBERSHIPS == 0) + this->groups_ = n; + else + this->groups_ = ACE_MIN (ACE_MAX (n, MCT_MIN_GROUPS), + IP_MAX_MEMBERSHIPS); + break; + } + case 'd': + this->debug_ = 1; + break; + case 'r': + { + ACE_TCHAR *c = getopt.opt_arg (); + if (ACE_OS::strcasecmp (c, ACE_TEXT ("CONSUMER")) == 0) + this->role_ = CONSUMER; + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("PRODUCER")) == 0) + this->role_ = PRODUCER; + else + { + help = 1; + retval = -1; + } + } + break; + case 'm': + { + //@todo add back OPT_BINDADDR_NO... + ACE_TCHAR *c = getopt.opt_arg (); + if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_BINDADDR_YES")) == 0) + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_BINDADDR_NO")) == 0) + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("DEFOPT_BINDADDR")) == 0) + { + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES); + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::DEFOPT_BINDADDR); + } + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_NULLIFACE_ALL")) == 0) + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_NULLIFACE_ALL); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("OPT_NULLIFACE_ONE")) == 0) + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_NULLIFACE_ALL); + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("DEFOPT_NULLIFACE")) == 0) + { + ACE_CLR_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::OPT_NULLIFACE_ALL); + ACE_SET_BITS (this->sdm_opts_, + ACE_SOCK_Dgram_Mcast::DEFOPT_NULLIFACE); + } + else if (ACE_OS::strcasecmp (c, ACE_TEXT ("DEFOPTS")) == 0) + this->sdm_opts_ = ACE_SOCK_Dgram_Mcast::DEFOPTS; + else + { + help = 1; + retval = -1; + } + } + break; + case 't': + this->ttl_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'w': + this->wait_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + case ':': + // This means an option requiring an argument didn't have one. + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" Option '%c' requires an argument but ") + ACE_TEXT ("none was supplied\n"), + getopt.opt_opt ())); + help = 1; + retval = -1; + break; + case '?': + case 'h': + default: + if (ACE_OS::strcmp (argv[getopt.opt_ind () - 1], ACE_TEXT ("-?")) != 0 + && getopt.opt_opt () != 'h') + // Don't allow unknown options. + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" Found an unknown option (%c) ") + ACE_TEXT ("we couldn't handle.\n"), + getopt.opt_opt ())); + // getopt.last_option ())); //readd with "%s" when + // last_option() is available. + help = 1; + retval = -1; + break; + } + } + + if (retval == -1) + { + if (help) + // print usage here + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s [options]\n") + ACE_TEXT ("Options:\n") + ACE_TEXT (" -g {STRING} --GroupStart={STRING} ") + ACE_TEXT ("starting multicast group address\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=239.255.0.1:16000)\n") + ACE_TEXT (" -n {#} --Groups={#} ") + ACE_TEXT ("number of groups (default=5)\n") + ACE_TEXT (" -d --Debug ") + ACE_TEXT ("debug flag (default=off)\n") + ACE_TEXT (" -r {STRING} --Role={STRING} ") + ACE_TEXT ("role {PRODUCER|CONSUMER|BOTH}\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=BOTH)\n") + ACE_TEXT (" -m {STRING} --SDM_options={STRING} ") + ACE_TEXT ("ACE_SOCK_Dgram_Mcast ctor options\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=DEFOPTS)\n") + ACE_TEXT (" -i {#} --Iterations={#} ") + ACE_TEXT ("number of iterations (default=100)\n") + ACE_TEXT (" -t {#} --TTL={#} ") + ACE_TEXT ("time to live (default=1)\n") + ACE_TEXT (" -w {#} --Wait={#} ") + ACE_TEXT ("number of seconds to wait on CONSUMER\n") + ACE_TEXT (" ") + ACE_TEXT ("(default=2)\n") + ACE_TEXT (" -h/? --help ") + ACE_TEXT ("show this message\n"), + argv[0])); + + return -1; + } + + return 0; +} + +void +MCT_Config::dump (void) const +{ + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" Dumping MCT_Config\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tIP_MAX_MEMBERSHIPS = %d\n"), + IP_MAX_MEMBERSHIPS)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tgroups_ = %d\n"), + this->groups_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\trole_ = %s\n"), + (ACE_BIT_ENABLED (this->role_, PRODUCER) + && ACE_BIT_ENABLED (this->role_, CONSUMER)) + ? ACE_TEXT ("PRODUCER/CONSUMER") + : ACE_BIT_ENABLED (this->role_, PRODUCER) + ? ACE_TEXT ("PRODUCER") + : ACE_TEXT ("CONSUMER"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tsdm_options_ = %d\n"), + this->sdm_opts_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\titerations_ = %d\n"), + this->iterations_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tttl_ = %d\n"), + this->ttl_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\twait_ = %d\n"), + this->wait_)); + // Note that this call to get_host_addr is the non-reentrant + // version, but it's okay for us. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tgroups_start_ = %s:%d\n"), + this->group_start_.get_host_addr (), + this->group_start_.get_port_number ())); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +int +MCT_Config::set_group (int port, const char *group) +{ + return group_start_.set (port, group); +} + +/******************************************************************************/ + +class MCT_Event_Handler : public ACE_Event_Handler +{ +public: + MCT_Event_Handler (ACE_SOCK_Dgram_Mcast::options options + = ACE_SOCK_Dgram_Mcast::DEFOPTS); + virtual ~MCT_Event_Handler (void); + + int join (const ACE_INET_Addr &mcast_addr, + int reuse_addr = 1, + const ACE_TCHAR *net_if = 0); + int leave (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if = 0); + + // = Event Handler hooks. + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask close_mask); + + virtual ACE_HANDLE get_handle (void) const; + +protected: + ACE_SOCK_Dgram_Mcast *mcast (void); + int find (const char *buf); + +private: + ACE_SOCK_Dgram_Mcast mcast_; + + // List of groups we've joined + ACE_Vector<ACE_CString*> address_vec_; + + // Flag used to set the 'finished' flag when the last event handler + // gets removed from the reactor. + static ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> active_handlers_; +}; + +ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> MCT_Event_Handler::active_handlers_ = 0; + +MCT_Event_Handler::MCT_Event_Handler (ACE_SOCK_Dgram_Mcast::options options) + : mcast_ (options) +{ + // Increment the number of active handlers in the reactor. Note this isn't + // really correct, but it should work for our simple example. + ++MCT_Event_Handler::active_handlers_; +} + +MCT_Event_Handler::~MCT_Event_Handler (void) +{ + size_t size = this->address_vec_.size (); + for (size_t i = 0; i < size; ++i) + { + delete this->address_vec_[i]; + this->address_vec_[i] = 0; + } +} + + +ACE_SOCK_Dgram_Mcast * +MCT_Event_Handler::mcast (void) +{ + return &this->mcast_; +} + +int +MCT_Event_Handler::find (const char *buf) +{ + size_t size = this->address_vec_.size (); + size_t i; + for (i = 0; i < size; ++i) + { + if (ACE_OS::strcasecmp (buf, this->address_vec_[i]->c_str ()) == 0) + return 0; + } + + // Not found, so output message we received along with a list of groups + // we've joined for debugging. + ACE_CString local; + for (i = 0; i < size; ++i) + { + local += "\t"; + local += this->address_vec_[i]->c_str (); + local += "\n"; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%C not in:\n%C"), + buf, local.c_str ())); + + return -1; +} + + +int +MCT_Event_Handler::join (const ACE_INET_Addr &mcast_addr, + int reuse_addr, + const ACE_TCHAR *net_if) +{ + char buf[MAX_STRING_SIZE]; + ACE_OS::sprintf (buf, "%s/%d", + mcast_addr.get_host_addr (), + mcast_addr.get_port_number ()); + + if (this->mcast_.join (mcast_addr, reuse_addr, net_if) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Event_Handler::join %C %p\n"), + buf, + ACE_TEXT ("failed")), + -1); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Joined %C\n"), buf)); + + ACE_CString *str; + ACE_NEW_RETURN (str, ACE_CString (buf), -1); + this->address_vec_.push_back (str); + return 0; +} + +int +MCT_Event_Handler::leave (const ACE_INET_Addr &mcast_addr, + const ACE_TCHAR *net_if) +{ + if (this->mcast_.leave (mcast_addr, net_if) == 0) + { + char buf[MAX_STRING_SIZE]; + size_t size = this->address_vec_.size (); + for (size_t i = 0; i < size; ++i) + { + ACE_OS::sprintf (buf, "%s/%d", + mcast_addr.get_host_addr (), + mcast_addr.get_port_number ()); + if (ACE_OS::strcasecmp (buf, this->address_vec_[i]->c_str ()) == 0) + { + this->address_vec_[i]->set (""); + break; + } + } + return 0; + } + return -1; +} + +int +MCT_Event_Handler::handle_input (ACE_HANDLE /*handle*/) +{ + char buf[MAX_STRING_SIZE]; + ACE_OS::memset (buf, 0, sizeof buf); + ACE_INET_Addr addr; + + if (this->mcast ()->recv (buf, sizeof buf, addr) == -1) + { + ++error; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Event_Handler::handle_input - ") + ACE_TEXT ("calling recv\n")), -1); + } + + // Zero length buffer means we are done. + if (ACE_OS::strlen (buf) == 0) + return -1; + else if (this->find (buf) == -1) + { + ++error; + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("MCT_Event_Handler::handle_input - ") + ACE_TEXT ("Received dgram for a group we didn't join ") + ACE_TEXT ("(%s) \n"), + buf)); + } + return 0; +} + +int +MCT_Event_Handler::handle_close (ACE_HANDLE /*fd*/, + ACE_Reactor_Mask /*close_mask*/) +{ + // If this is the last handler, use the finished flag to signal + // the task to exit. + if (--MCT_Event_Handler::active_handlers_ == 0) + finished = 1; + + // The DONT_CALL flag keeps the reactor from calling handle_close () + // again, since we commit suicide below. + this->reactor ()->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); + this->reactor (0); + delete this; + return 0; +} + +ACE_HANDLE +MCT_Event_Handler::get_handle (void) const +{ + return this->mcast_.get_handle (); +} + +/******************************************************************************/ + +/* + * Our MCT_Task object will be an Active Object if we are running the Consumer + * side of the test. open() calls active() which creates a thread and calls + * the svc() method that calls runs the reactor event loop. + */ +class MCT_Task : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + MCT_Task (const MCT_Config &config, + ACE_Reactor *reactor = ACE_Reactor::instance ()); + ~MCT_Task (void); + + //FUZZ: disable check_for_lack_ACE_OS + // = Task hooks. + virtual int open (void *args = 0); + virtual int svc (void); + //FUZZ: enable check_for_lack_ACE_OS + +private: + const MCT_Config &config_; + int iterations_; +}; + +MCT_Task::MCT_Task (const MCT_Config &config, + ACE_Reactor *reactor) + : config_ (config) +{ + this->reactor (reactor); +} + +MCT_Task::~MCT_Task (void) +{} + +int +MCT_Task::open (void *) +{ + MCT_Event_Handler *handler; + + ACE_INET_Addr addr = this->config_.group_start (); + int groups = this->config_.groups (); + for (int i = 0; i < groups; ++i) + { + ACE_NEW_RETURN (handler, + MCT_Event_Handler (this->config_.options ()), -1); + // We subscribe to all groups for the first one and one each for + // all the others. + if (i == 0) + { + // go ahead and hide the other one since we want our own. + ACE_INET_Addr addr = this->config_.group_start (); + for (int j = 0; j < groups; ++j) + { + // If OPT_BINDADDR_YES is set, this will fail after the first + // join, so just break and keep on going, otherwise it's a + // real error. + if (j > 0 + && ACE_BIT_ENABLED (ACE_SOCK_Dgram_Mcast::OPT_BINDADDR_YES, + this->config_.options ())) + break; + + if (handler->join (addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Task::open - join error\n")), + -1); + advance_addr (addr); + } + } + else + { + if (handler->join (addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Task::open - join error\n")), + -1); + } + + advance_addr (addr); + + if (this->reactor ()->register_handler (handler, READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("MCT_Task::open - cannot register ") + ACE_TEXT ("handler\n")), + -1); + } + + if (this->activate (THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("MCT_TASK:open - activate failed")), + -1); + return 0; +} + +int +MCT_Task::svc (void) +{ + // make sure this thread owns the reactor or handle_events () won't do + // anything. + this->reactor ()->owner (ACE_Thread::self ()); + + // loop and call handle_events... + while (!finished) + this->reactor ()->handle_events (); + + return 0; +} + +/******************************************************************************/ + +int send_dgram (ACE_SOCK_Dgram &socket, ACE_INET_Addr addr, int done = 0) +{ + + // Send each message twice, once to the right port, and once to the "wrong" + // port. This helps generate noise and lets us see if port filtering is + // working properly. + const char *address = addr.get_host_addr (); + int port = addr.get_port_number (); + + for (int i = 0; i < 2; ++i) + { + char buf[MAX_STRING_SIZE]; + if (done) + buf[0] = 0; + else + ACE_OS::sprintf (buf, "%s/%d", address, port); + //ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("sending (%s)\n"), buf)); + if (socket.send (buf, ACE_OS::strlen (buf),addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("send_dgram - error calling send on ") + ACE_TEXT ("ACE_SOCK_Dgram.")), -1); + addr.set_port_number (++port); + } + return 0; +} + +int producer (MCT_Config &config) +{ + int retval = 0; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Starting producer...\n"))); + ACE_SOCK_Dgram socket (ACE_sap_any_cast (ACE_INET_Addr &)); + //FUZZ: enable check_for_lack_ACE_OS + + // set the TTL or hop count based on the config.ttl () value + if (config.ttl () > 1 && config.group_start().get_type() == AF_INET) + { + int ttl = config.ttl (); + if (socket.set_option (IPPROTO_IP, + IP_MULTICAST_TTL, + (void*) &ttl, + sizeof ttl) != 0) + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("could net set socket option IP_MULTICAST_TTL ") + ACE_TEXT ("= %d\n"), + ttl)); + else + ACE_DEBUG ((LM_INFO, ACE_TEXT ("set IP_MULTICAST_TTL = %d\n"), ttl)); + } +#if defined (ACE_HAS_IPV6) + else + { + // for IPv6, a hop limit is used instead of TTL + int hops = config.ttl (); + if (socket.set_option (IPPROTO_IPV6, + IPV6_MULTICAST_HOPS, + (void*) &hops, + sizeof hops) != 0) + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("could net set socket option IPV6_MULTICAST_HOPS") + ACE_TEXT (" = %d\n"), + hops)); + else + ACE_DEBUG ((LM_INFO, ACE_TEXT ("set IPV6_MULTICAST_HOPS = %d\n"), + hops)); + } + + // Turn on multicast loopback since the test relies on it and the + // ACE_SOCK_Dgram_Mcast documents the loopback state as indeterminate. + int do_loopback = 1; + if (socket.set_option (IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, + (void *)&do_loopback, + sizeof (do_loopback)) == -1) + { + if (errno == ENOTSUP) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("IPV6_MULTICAST_LOOP not supported\n"))); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Can't set IPV6_MULTICAST_LOOP"))); + } +#endif /* ACE_HAS_IPV6 */ + + + int iterations = config.iterations (); + // we add an extra 5 groups for noise. + int groups = config.groups () + 5; + for (int i = 0; (i < iterations || iterations == 0) && !finished; ++i) + { + ACE_INET_Addr addr = config.group_start (); + for (int j = 0; j < groups && !finished; ++j) + { + if ((retval += send_dgram (socket, addr, + ((i + 1) == iterations))) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Calling send_dgram.\n"))); + if ((retval += advance_addr (addr)) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Calling advance_addr.\n"))); + } + // Give the task thread a chance to run. + ACE_Thread::yield (); + } + return retval; +} + +/* + * Advance the address by 1, e.g., 239.255.0.1 => 239.255.0.2 + * Note that the algorithm is somewhat simplistic, but sufficient for our + * purpose. + */ +int advance_addr (ACE_INET_Addr &addr) +{ + int a, b, c, d; + if (addr.get_type () == AF_INET) + { + ::sscanf (addr.get_host_addr (), "%d.%d.%d.%d", &a, &b, &c, &d); + if (d < 255) + ++d; + else if (c < 255) + { + d = 1; + ++c; + } + else if (b < 255) + { + d = 1; + c = 0; + ++b; + } + else if (a < 239) + { + d = 1; + c = 0; + b = 0; + ++a; + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("advance_addr - Cannot advance multicast ") + ACE_TEXT ("group address past %s\n"), + addr.get_host_addr ()), + -1); + + ACE_TCHAR buf[MAX_STRING_SIZE]; + ACE_OS::sprintf (buf, ACE_TEXT ("%d.%d.%d.%d:%d"), + a, b, c, d, addr.get_port_number ()); + addr.set (buf); + return 0; + } +#if defined (ACE_HAS_IPV6) + else // assume AF_INET6 + { + sockaddr_in6 *saddr = reinterpret_cast<sockaddr_in6 *> (addr.get_addr ()); + unsigned char *sin6_addr = reinterpret_cast<unsigned char *> (&saddr->sin6_addr); + int i = 15; + + // i >= 2 is used here so that the flags and scope for the + // multicast address are not changed + while (i >= 2 && sin6_addr[i] == 0xff) + { + sin6_addr[i] = 0; + i--; + } + + if (i >= 2) + { + sin6_addr[i]++; + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("advance_addr - Cannot advance ") + ACE_TEXT ("multicast group address past %s\n"), + addr.get_host_addr ()), + -1); + + } + } +#endif /* ACE_HAS_IPV6 */ + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + int retval = 0; + MCT_Config config; + retval = config.open (argc, argv); + if (retval != 0) + return 1; + + const ACE_TCHAR *temp = ACE_TEXT ("Multicast_Test_IPV6"); + ACE_TString test = temp; + + u_long role = config.role (); + if (ACE_BIT_DISABLED (role, MCT_Config::PRODUCER) + || ACE_BIT_DISABLED (role, MCT_Config::CONSUMER)) + { + if (ACE_BIT_ENABLED (role, MCT_Config::PRODUCER)) + test += ACE_TEXT ("-PRODUCER"); + else + test += ACE_TEXT ("-CONSUMER"); + } + + // Start test only if options are valid. + ACE_START_TEST (test.c_str ()); + +#if defined (ACE_HAS_IPV6) + +# if !defined (ACE_LACKS_UNIX_SIGNALS) + // Register a signal handler to close down application gracefully. + ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); +# endif + + // Dump the configuration info to the log if caller passed debug option. + if (config.debug ()) + config.dump (); + + ACE_Reactor *reactor = ACE_Reactor::instance (); + + MCT_Task *task = new MCT_Task (config, reactor); + + if (ACE_BIT_ENABLED (role, MCT_Config::CONSUMER)) + { + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Starting consumer...\n"))); + // Open makes it an active object. + retval += task->open (); + } + + // now produce the datagrams... + if (ACE_BIT_ENABLED (role, MCT_Config::PRODUCER)) + retval += producer (config); + + if (ACE_BIT_ENABLED (role, MCT_Config::CONSUMER)) + { + // and wait for everything to finish + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("start waiting for consumer to finish...\n"))); + // Wait for the threads to exit. + // But, wait for a limited time since we could hang if the last udp + // message isn't received. + ACE_Time_Value max_wait ( config.wait ()/* seconds */); + ACE_Time_Value wait_time (ACE_OS::gettimeofday () + max_wait); + ACE_Time_Value *ptime = ACE_BIT_ENABLED (role, MCT_Config::PRODUCER) + ? &wait_time : 0; + if (ACE_Thread_Manager::instance ()->wait (ptime) == -1) + { + if (errno == ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("maximum wait time of %d msec exceeded\n"), + max_wait.msec ())); + else + ACE_OS::perror (ACE_TEXT ("wait")); + + ++error; + } + } + + delete task; +#endif /* ACE_HAS_IPV6 */ + ACE_END_TEST; + return (retval == 0 && error == 0) ? 0 : 1; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Multicast_Test_IPV6")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("This test must be run on a platform ") + ACE_TEXT ("that support IP multicast and threads.\n"))); + + ACE_END_TEST; + + return 0; +} +#endif /* ACE_HAS_IP_MULTICAST && ACE_HAS_THREADS */ diff --git a/ACE/tests/Multihomed_INET_Addr_Test.cpp b/ACE/tests/Multihomed_INET_Addr_Test.cpp new file mode 100644 index 00000000000..8c89f1fe7db --- /dev/null +++ b/ACE/tests/Multihomed_INET_Addr_Test.cpp @@ -0,0 +1,470 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Multihomed_INET_Addr_Test.cpp +// +// = DESCRIPTION +// Performs several tests on the Multihomed_ACE_INET_Addr class. +// It creates several IPv4 addresses and checks that the +// address formed by the class is valid. +// +// = AUTHOR +// Edward Mulholland (emulholl@atl.lmco.com) +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Multihomed_INET_Addr.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_arpa_inet.h" + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Multihomed_INET_Addr_Test")); + + int status = 0; // Innocent until proven guilty + + // loop variables + size_t i, j; + sockaddr_in *pointer; + + // The port will always be this + u_short port = 80; + + // The primary address will always be this + const char *primary_dotted_decimal = "138.38.180.251"; + + // The secondary addresses will always be these... + const char *secondary_dotted_decimals[] = { + "64.219.54.121", + "127.0.0.1", + "21.242.14.51", + "53.141.124.24", + "42.12.44.9" + }; + + // ... and as you can see, there are 5 of them + const size_t num_secondaries = 5; + + // We also need the primary address and the secondary addresses + // in ACE_UINT32 format in host byte order + ACE_UINT32 primary_addr32; + ACE_UINT32 secondary_addr32[5]; + + { + struct in_addr addrv4; + ACE_OS::inet_pton (AF_INET, primary_dotted_decimal, &addrv4); + ACE_OS::memcpy (&primary_addr32, &addrv4, sizeof (primary_addr32)); + primary_addr32 = ACE_NTOHL(primary_addr32); + } + + for (i = 0; i < num_secondaries; ++i) { + struct in_addr addrv4; + ACE_OS::inet_pton (AF_INET, secondary_dotted_decimals[i], &addrv4); + ACE_OS::memcpy (&secondary_addr32[i], &addrv4, sizeof (primary_addr32)); + secondary_addr32[i] = ACE_NTOHL(secondary_addr32[i]); + } + + // Test subject + ACE_Multihomed_INET_Addr addr; + + // Array of ones (used to clear the secondary addresses of the test + // subject) + ACE_UINT32 array_of_threes[5] = { ACE_UINT32 (3), + ACE_UINT32 (3), + ACE_UINT32 (3), + ACE_UINT32 (3), + ACE_UINT32 (3) }; + + // Array of INET_Addrs that will repeatedly be passed into the + // get_secondary_addresses accessor of Multihomed_INET_Addr + ACE_INET_Addr in_out[5]; + + // Array of INET_Addrs against which the above array will be tested. + ACE_INET_Addr stay_out[5]; + + // Array of sockaddrs that will repeatedly be passed into the + // get_addresses accessor of Multihomed_INET_Addr + const size_t num_sockaddrs = 6; + sockaddr_in in_out_sockaddr[num_sockaddrs]; + + // Run the test with a varying number of secondary addresses + for (i = 0; i <= num_secondaries; ++i) { + + + /****** Clear the in_out array and test subject ******/ + + + // Clear the in_out array by setting every port to 0 and every + // address to 1 + for (j = 0; j < num_secondaries; ++j) { + in_out[j].set(0, ACE_UINT32 (1), 1); + } + + // Clear the in_out_sockaddr array by setting every port to 0 and + // every address to 1 + ACE_OS::memset(in_out_sockaddr, 0, num_sockaddrs * sizeof(sockaddr)); + + // Clear the test subject by setting the port to 2 and every + // address (both the primary and the secondaries) to 3 + addr.set (2, ACE_UINT32 (3), 1, array_of_threes, num_secondaries); + + // Check that the port is 2 + if (addr.get_port_number() != 2) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_port_number check\n") + ACE_TEXT ("%d != %d\n"), + addr.get_port_number(), + 2)); + status = 1; + } + + // Check that the primary address is 3 + if (addr.get_ip_address() != ACE_UINT32 (3)) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_ip_address check\n") + ACE_TEXT ("0x%x != 0x%x\n"), + addr.get_ip_address(), + ACE_UINT32 (3))); + status = 1; + } + + // Check that the test subject reports the correct number of + // secondary addresses. + size_t returned_num_secondaries = addr.get_num_secondary_addresses(); + if (returned_num_secondaries == num_secondaries) { + + // Set a stay_out element to the state that we expect to see + // from every in_out element after the in_out array is passed to + // the accessor of the test subject. + stay_out[0].set(2, ACE_UINT32 (3), 1); + + // Pass the in_out array to the accessor + addr.get_secondary_addresses(in_out, num_secondaries); + + // Check that the in_out array matches stay_out element + for (j = 0; j < num_secondaries; ++j) { + + if (in_out[j] != stay_out[0]) { + + ACE_TCHAR in_out_string[100]; + ACE_TCHAR stay_out_string[100]; + + in_out[j].addr_to_string(in_out_string, 100); + stay_out[0].addr_to_string(stay_out_string, 100); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_secondary_addresses check\n") + ACE_TEXT ("%s != %s\n"), + in_out_string, + stay_out_string)); + + status = 1; + } + } + + // Pass the in_out_sockaddr array to the accessor + addr.get_addresses(in_out_sockaddr, num_secondaries + 1); + + // Check that the in_out_sockaddr array matches stay_out element + for (j = 0, pointer = in_out_sockaddr; + j < num_secondaries + 1; + ++j, ++pointer) { + + if (ACE_OS::memcmp(pointer, stay_out[0].get_addr(), sizeof(sockaddr))) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_addresses check\n"))); + + status = 1; + } + } + + } else { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_num_secondary_addresses check\n") + ACE_TEXT ("%d != %d\n"), + returned_num_secondaries, + num_secondaries)); + status = 1; + + } + + + /**** Test set (u_short, const char[], int, int, const char *([]), size_t) ****/ + + + addr.set(port, + primary_dotted_decimal, + 1, + AF_INET, + secondary_dotted_decimals, + i); + + // Check the port number + if (addr.get_port_number() != port) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_port_number check\n") + ACE_TEXT ("%d != %d\n"), + addr.get_port_number(), + port)); + status = 1; + } + + // Check the primary address + if (0 != ACE_OS::strcmp (addr.get_host_addr(), primary_dotted_decimal)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed get_host_addr() check\n") + ACE_TEXT ("%s != %s\n"), + primary_dotted_decimal, + addr.get_host_addr (), + primary_dotted_decimal)); + status = 1; + } + + // Check that the test subject reports the correct number of + // secondary addresses. + returned_num_secondaries = addr.get_num_secondary_addresses(); + if (returned_num_secondaries == i) { + + // Initialize the stay_out array with the secondary addresses + for (j = 0; j < i; ++j) { + stay_out[j].set(port, secondary_dotted_decimals[j]); + } + + // Pass the in_out array to the accessor + addr.get_secondary_addresses(in_out, i); + + // Check that the in_out array matches stay_out array + for (j = 0; j < i; ++j) { + + if (in_out[j] != stay_out[j]) { + + ACE_TCHAR in_out_string[100]; + ACE_TCHAR stay_out_string[100]; + + in_out[j].addr_to_string(in_out_string, 100); + stay_out[j].addr_to_string(stay_out_string, 100); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_secondary_addresses check\n") + ACE_TEXT ("%s != %s\n"), + in_out_string, + stay_out_string)); + + status = 1; + } + } + + // Pass the in_out_sockaddr array to the accessor + addr.get_addresses(in_out_sockaddr, i + 1); + + // Check that the primary address in the in_out_sockaddr array + // matches the primary address reported by the superclass + if (ACE_OS::memcmp(in_out_sockaddr, addr.get_addr(), sizeof(sockaddr))) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_addresses check ") + ACE_TEXT ("(for primary address)\n"))); + + status = 1; + + } + + // Check that the secondary addresses in the in_out_sockaddr + // array match the stay_out array + for (j = 1, pointer = &in_out_sockaddr[1]; + j < i + 1; + ++j, ++pointer) { + + if (ACE_OS::memcmp(pointer, stay_out[j-1].get_addr(), sizeof(sockaddr))) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_addresses check ") + ACE_TEXT ("(for secondary addresses)\n"))); + + status = 1; + } + } + + } else { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_num_secondary_addresses check\n") + ACE_TEXT ("%d != %d\n"), + returned_num_secondaries, + i)); + status = 1; + } + + + /****** Clear the in_out array and test subject AGAIN ******/ + + + // Clear the in_out array by setting every port to 0 and every + // address to 1 + for (j = 0; j < num_secondaries; ++j) { + in_out[j].set(0, ACE_UINT32 (1), 1); + } + + // Clear the test subject by setting the port to 2 and every + // address (both the primary and the secondaries) to 3 + addr.set (2, ACE_UINT32 (3), 1, array_of_threes, num_secondaries); + + // Check that the port is 2 + if (addr.get_port_number() != 2) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed third get_port_number check\n") + ACE_TEXT ("%d != %d\n"), + addr.get_port_number(), + 2)); + status = 1; + } + + // Check that the primary address is 3 + if (addr.get_ip_address() != ACE_UINT32 (3)) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed third get_ip_address check\n") + ACE_TEXT ("0x%x != 0x%x\n"), + addr.get_ip_address(), + ACE_UINT32 (3))); + status = 1; + } + + // Check that the test subject reports the correct number of + // secondary addresses. + returned_num_secondaries = addr.get_num_secondary_addresses(); + if (returned_num_secondaries == num_secondaries) { + + // Set a stay_out element to the state that we expect to see + // from every in_out element after the in_out array is passed to + // the accessor of the test subject. + stay_out[0].set(2, ACE_UINT32 (3), 1); + + // Pass the in_out array to the accessor + addr.get_secondary_addresses(in_out, num_secondaries); + + // Check that the in_out array matches stay_out array + for (j = 0; j < num_secondaries; ++j) { + + if (in_out[j] != stay_out[0]) { + + ACE_TCHAR in_out_string[100]; + ACE_TCHAR stay_out_string[100]; + + in_out[j].addr_to_string(in_out_string, 100); + stay_out[0].addr_to_string(stay_out_string, 100); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed third get_secondary_addresses check\n") + ACE_TEXT ("%s != %s\n"), + in_out_string, + stay_out_string)); + + status = 1; + } + } + + } else { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed third get_num_secondary_addresses check\n") + ACE_TEXT ("%d != %d\n"), + returned_num_secondaries, + num_secondaries)); + status = 1; + + } + + + /**** Test set (u_short, ACE_UINT32, int, const ACE_UINT32 *, size_t) ****/ + + addr.set(port, + primary_addr32, + 1, + secondary_addr32, + i); + + // Check the port number + if (addr.get_port_number() != port) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed forth get_port_number check\n") + ACE_TEXT ("%d != %d\n"), + addr.get_port_number(), + port)); + status = 1; + } + + // Check the primary address + if (0 != ACE_OS::strcmp (addr.get_host_addr(), primary_dotted_decimal)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed second get_ip_address() check\n") + ACE_TEXT ("%s != %s\n"), + primary_dotted_decimal, + addr.get_host_addr (), + primary_dotted_decimal)); + status = 1; + } + + // Check that the test subject reports the correct number of + // secondary addresses. + returned_num_secondaries = addr.get_num_secondary_addresses(); + if (returned_num_secondaries == i) { + + // Initialize the stay_out array with the secondary addresses + for (j = 0; j < i; ++j) { + stay_out[j].set(port, secondary_addr32[j]); + } + + // Pass the in_out array to the accessor + addr.get_secondary_addresses(in_out, j); + + // Check that the in_out array matches stay_out array + for (j = 0; j < i; ++j) { + + if (in_out[j] != stay_out[j]) { + + ACE_TCHAR in_out_string[100]; + ACE_TCHAR stay_out_string[100]; + + in_out[j].addr_to_string(in_out_string, 100); + stay_out[j].addr_to_string(stay_out_string, 100); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed forth get_secondary_addresses check\n") + ACE_TEXT ("%s != %s\n"), + in_out_string, + stay_out_string)); + + status = 1; + } + } + + } else { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed forth get_num_secondary_addresses check\n") + ACE_TEXT ("%d != %d\n"), + returned_num_secondaries, + i)); + status = 1; + } + + } + + ACE_END_TEST; + return status; + +} diff --git a/ACE/tests/Multihomed_INET_Addr_Test_IPV6.cpp b/ACE/tests/Multihomed_INET_Addr_Test_IPV6.cpp new file mode 100644 index 00000000000..1607dd81fb2 --- /dev/null +++ b/ACE/tests/Multihomed_INET_Addr_Test_IPV6.cpp @@ -0,0 +1,191 @@ +// $Id$ +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Multihomed_INET_Addr_Test.cpp +// +// = DESCRIPTION +// Performs several tests on the Multihomed_ACE_INET_Addr class. +// It creates several IPv6 addresses and checks that the +// address formed by the class is valid. +// +// = AUTHOR +// Edward Mulholland (emulholl@atl.lmco.com) +// Brian Buesker (bbuesker@qualcomm.com) - Added testing of +// ACE_Multihomed_INET_Addr class +// using IPv6 addresses based on +// Multihomed_INET_Addr_Test. +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Multihomed_INET_Addr.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_arpa_inet.h" + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Multihomed_INET_Addr_Test_IPV6")); + + int status = 0; // Innocent until proven guilty + +#if defined (ACE_HAS_IPV6) + // loop variables + size_t i, j; + sockaddr_in6 *pointer6; + + const ACE_TCHAR *primary_ipv6 = ACE_TEXT("3ffe::123:4567:89ab:cdef"); + + const ACE_TCHAR *secondary_ipv6[] = { + ACE_IPV6_LOCALHOST, + ACE_TEXT("fe80::0123:4567:89ab:cdef"), + ACE_TEXT("fec0::0123:4567:89ab:cdef"), + ACE_TEXT("3ffe::1:0123:4567:89ab:cdef"), + ACE_TEXT("2002:3e02:5473::") + }; + + // The port will always be this + u_short port = 80; + + // ... and as you can see, there are 5 of them + const size_t num_secondaries = 5; + + // Test subject + ACE_Multihomed_INET_Addr addr; + + // Array of INET_Addrs that will repeatedly be passed into the + // get_secondary_addresses accessor of Multihomed_INET_Addr + ACE_INET_Addr in_out[5]; + + // Array of INET_Addrs against which the above array will be tested. + ACE_INET_Addr stay_out[5]; + + // Array of sockaddrs that will repeatedly be passed into the + // get_addresses accessor of Multihomed_INET_Addr + const size_t num_sockaddrs = 6; + sockaddr_in6 in_out_sockaddr6[num_sockaddrs]; + + for (i = 0; i <= num_secondaries; ++i) { + + + /**** Test set (u_short, const char[], int, int, const char *([]), size_t) ****/ + + + addr.set(port, + primary_ipv6, + 1, + AF_INET6, + secondary_ipv6, + i); + + // Check the port number + if (addr.get_port_number() != port) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_port_number check\n") + ACE_TEXT ("%d != %d\n"), + addr.get_port_number(), + port)); + status = 1; + } + + // Check the primary address + if (0 != ACE_OS::strcmp (ACE_TEXT_CHAR_TO_TCHAR(addr.get_host_addr()), primary_ipv6)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%s failed get_host_addr() check\n") + ACE_TEXT ("%s != %s\n"), + primary_ipv6, + addr.get_host_addr (), + primary_ipv6)); + status = 1; + } + + // Check that the test subject reports the correct number of + // secondary addresses. + size_t returned_num_secondaries = addr.get_num_secondary_addresses(); + if (returned_num_secondaries == i) { + + // Initialize the stay_out array with the secondary addresses + for (j = 0; j < i; ++j) { + stay_out[j].set(port, secondary_ipv6[j]); + } + + // Pass the in_out array to the accessor + addr.get_secondary_addresses(in_out, i); + + // Check that the in_out array matches stay_out array + for (j = 0; j < i; ++j) { + + if (in_out[j] != stay_out[j]) { + + ACE_TCHAR in_out_string[100]; + ACE_TCHAR stay_out_string[100]; + + in_out[j].addr_to_string(in_out_string, 100); + stay_out[j].addr_to_string(stay_out_string, 100); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_secondary_addresses check\n") + ACE_TEXT ("%s != %s\n"), + in_out_string, + stay_out_string)); + + status = 1; + } + } + + // Pass the in_out_sockaddr array to the accessor + addr.get_addresses(in_out_sockaddr6, i + 1); + + // Check that the primary address in the in_out_sockaddr array + // matches the primary address reported by the superclass + if (ACE_OS::memcmp(in_out_sockaddr6, addr.get_addr(), + sizeof(sockaddr_in6))) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed second get_addresses check ") + ACE_TEXT ("(for primary address)\n"))); + + status = 1; + + } + + // Check that the secondary addresses in the in_out_sockaddr + // array match the stay_out array + for (j = 1, pointer6 = &in_out_sockaddr6[1]; + j < i + 1; + ++j, ++pointer6) { + + if (ACE_OS::memcmp(pointer6, stay_out[j-1].get_addr(), + sizeof(sockaddr_in6))) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_addresses check ") + ACE_TEXT ("(for secondary addresses)\n"))); + + status = 1; + } + } + } else { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed get_num_secondary_addresses check\n") + ACE_TEXT ("%d != %d\n"), + returned_num_secondaries, + i)); + status = 1; + } + } +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + return status; + +} diff --git a/ACE/tests/Naming_Test.cpp b/ACE/tests/Naming_Test.cpp new file mode 100644 index 00000000000..1a905291184 --- /dev/null +++ b/ACE/tests/Naming_Test.cpp @@ -0,0 +1,322 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Naming_Test.cpp +// +// = DESCRIPTION +// This is a test to illustrate the Naming Services. The test +// does binds, rebinds, finds, and unbinds on name bindings using +// the local naming context. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "randomize.h" +#include "ace/ACE.h" +#include "ace/SString.h" +#include "ace/Naming_Context.h" +#include "ace/Profile_Timer.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Naming_Test, "$Id$") + +static char name[BUFSIZ]; +static char value[BUFSIZ]; +static char type[BUFSIZ]; + +void +initialize_array (int * array, int size) +{ + for (int n = 0; n < size; ++n) + array[n] = n; +} + +static void +print_time (ACE_Profile_Timer &timer, + const char *test) +{ + ACE_Profile_Timer::ACE_Elapsed_Time et; + timer.stop (); + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" ***** %C ***** \n"), test)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, et.user_time, et.system_time)); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("time per call = %f usecs\n"), + (et.real_time / double (ACE_NS_MAX_ENTRIES)) * 1000000)); +} + +static void +test_bind (ACE_Naming_Context &ns_context) +{ + int array [ACE_NS_MAX_ENTRIES]; + + initialize_array (array, sizeof (array) / sizeof (array[0])); + randomize (array, sizeof (array) / sizeof (array[0])); + + // do the binds + for (size_t i = 0; i < ACE_NS_MAX_ENTRIES; i++) + { + ACE_OS::sprintf (name, "%s%d", "name", array[i]); + ACE_NS_WString w_name (name); + + ACE_OS::sprintf (value, "%s%d", "value", array[i]); + ACE_NS_WString w_value (value); + + ACE_OS::sprintf (type, "%s%d", "type", array [i]); + int bind_result = ns_context.bind (w_name, w_value, type); + ACE_ASSERT (bind_result != -1); + } +} + +static void +test_find_failure (ACE_Naming_Context &ns_context) +{ + ACE_OS::sprintf (name, "%s", "foo-bar"); + ACE_NS_WString w_name (name); + ACE_NS_WString w_value; + char *l_type = 0; + + // Do the finds. + for (size_t i = 0; i < ACE_NS_MAX_ENTRIES; i++) + { + int resolve = ns_context.resolve (w_name, w_value, l_type); + ACE_ASSERT (resolve == -1); + } +} + +static void +test_rebind (ACE_Naming_Context &ns_context) +{ + int array [ACE_NS_MAX_ENTRIES]; + + initialize_array (array, sizeof (array) / sizeof (array[0])); + randomize (array, sizeof (array) / sizeof (array[0])); + + // do the rebinds + for (size_t i = 0; i < ACE_NS_MAX_ENTRIES; i++) + { + ACE_OS::sprintf (name, "%s%d", "name", array[i]); + ACE_NS_WString w_name (name); + + ACE_OS::sprintf (value, "%s%d", "value", -array[i]); + ACE_NS_WString w_value (value); + + ACE_OS::sprintf (type, "%s%d", "type", -array[i]); + int rebind = ns_context.rebind (w_name, w_value, type); + ACE_ASSERT (rebind != -1); + } +} + +static void +test_unbind (ACE_Naming_Context &ns_context) +{ + int array [ACE_NS_MAX_ENTRIES]; + + initialize_array (array, sizeof (array) / sizeof (array[0])); + randomize (array, sizeof (array) / sizeof (array[0])); + + // do the unbinds + for (size_t i = 0; i < ACE_NS_MAX_ENTRIES; i++) + { + ACE_OS::sprintf (name, "%s%d", "name", array[i]); + ACE_NS_WString w_name (name); + int unbind = ns_context.unbind (w_name); + ACE_ASSERT (unbind != -1); + } +} + +static void +test_find (ACE_Naming_Context &ns_context, int sign, int result) +{ + char temp_val[BUFSIZ]; + char temp_type[BUFSIZ]; + + int array [ACE_NS_MAX_ENTRIES]; + + initialize_array (array, sizeof (array) / sizeof (array[0])); + randomize (array, sizeof (array) / sizeof (array[0])); + + // do the finds + for (size_t i = 0; i < ACE_NS_MAX_ENTRIES; i++) + { + if (sign == 1) + { + ACE_OS::sprintf (temp_val, "%s%d", "value", array[i]); + ACE_OS::sprintf (temp_type, "%s%d", "type", array[i]); + } + else + { + ACE_OS::sprintf (temp_val, "%s%d", "value", -array[i]); + ACE_OS::sprintf (temp_type, "%s%d", "type", -array[i]); + } + + ACE_OS::sprintf (name, "%s%d", "name", array[i]); + + ACE_NS_WString w_name (name); + ACE_NS_WString w_value; + char *type_out = 0; + ACE_NS_WString val (temp_val); + + int const resolve_result = ns_context.resolve (w_name, w_value, type_out); + if (resolve_result != result) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error, resolve result not equal to resutlt (%d != %d)\n"), + resolve_result, result)); + + char *l_value = w_value.char_rep (); + + if (l_value) + { + ACE_ASSERT (w_value == val); + if (ns_context.name_options ()->debug ()) + { + if (type_out) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Name: %C\tValue: %C\tType: %C\n"), + name, l_value, type_out)); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Name: %C\tValue: %C\n"), + name, l_value)); + } + + if (type_out) + { + ACE_ASSERT (ACE_OS::strcmp (type_out, temp_type) == 0); + delete[] type_out; + } + } + + delete[] l_value; + } +} + + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Naming_Test")); + ACE_TCHAR temp_file [BUFSIZ]; + ACE_Naming_Context *ns_context = 0; + ACE_NEW_RETURN (ns_context, ACE_Naming_Context, -1); + + ACE_Name_Options *name_options = ns_context->name_options (); + + name_options->parse_args (argc, argv); + /* + ** NOTE! This is an experimental value and is not magic in any way. It + ** works for me, on one system. It's needed because in the particular + ** case here where the underlying mmap will allocate a small area and + ** then try to grow it, it always moves it to a new location, which + ** totally screws things up. I once tried forcing the realloc to do + ** MAP_FIXED but that's not a good solution since it may overwrite other + ** mapped areas of memory, like the heap, or the C library, and get very + ** unexpected results. (Steve Huston, 24-August-2007) + */ +# if defined (linux) && defined (__x86_64__) + name_options->base_address ((char*)0x3c00000000); +#endif + int unicode = 0; +#if (defined (ACE_WIN32) && defined (ACE_USES_WCHAR)) + unicode = 1; +#endif /* ACE_WIN32 && ACE_USES_WCHAR */ + if (unicode && name_options->use_registry () == 1) + { + name_options->namespace_dir (ACE_TEXT ("Software\\ACE\\Name Service")); + name_options->database (ACE_TEXT ("Version 1")); + } + else + { + // Allow the user to determine where the context file will be + // located just in case the current directory is not suitable for + // locking. We don't just set namespace_dir () on name_options + // because that is not sufficient to work around locking problems + // for Tru64 when the current directory is NFS mounted from a + // system that does not properly support locking. + const char* temp_envs[] = { "TMPDIR", "TEMP", "TMP", 0 }; + for(const char** temp_env = temp_envs; *temp_env != 0; ++temp_env) + { + char* temp_dir = ACE_OS::getenv(*temp_env); + if (temp_dir != 0) + { + ACE_OS::chdir (temp_dir); + break; + } + } + ACE_OS::strcpy (temp_file, ACE::basename (name_options->process_name (), + ACE_DIRECTORY_SEPARATOR_CHAR)); + ACE_OS::strcat (temp_file, ACE_TEXT ("XXXXXX")); + + // Set the database name using mktemp to generate a unique file name + name_options->database (ACE_OS::mktemp (temp_file)); + } + + if (ns_context->open (ACE_Naming_Context::PROC_LOCAL, 1) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("ns_context->open (PROC_LOCAL) %p\n"), + ACE_TEXT ("failed")), + -1); + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("time to test %d iterations using %s\n"), + ACE_NS_MAX_ENTRIES, name_options->use_registry () ? + ACE_TEXT ("Registry") : ACE_TEXT ("ACE"))); + + ACE_Profile_Timer timer; + + timer.start (); + // Add some bindings to the database + test_bind (*ns_context); + print_time (timer, "Binds"); + + timer.start (); + // Should find the entries + test_find (*ns_context, 1, 0); + print_time (timer, "Successful Finds"); + + timer.start (); + // Rebind with negative values + test_rebind (*ns_context); + print_time (timer, "Rebinds"); + + timer.start (); + // Should find the entries + test_find (*ns_context, -1, 0); + print_time (timer, "Successful Finds"); + + timer.start (); + // Should not find the entries + test_find_failure (*ns_context); + print_time (timer, "UnSuccessful Finds"); + + timer.start (); + // Remove all bindings from database + test_unbind (*ns_context); + print_time (timer, "Unbinds"); + + ACE_OS::sprintf (temp_file, ACE_TEXT ("%s%s%s"), + name_options->namespace_dir (), + ACE_DIRECTORY_SEPARATOR_STR, + name_options->database ()); + + delete ns_context; + + // Remove any existing files. No need to check return value here + // since we don't care if the file doesn't exist. + ACE_OS::unlink (temp_file); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Network_Adapters_Test.cpp b/ACE/tests/Network_Adapters_Test.cpp new file mode 100644 index 00000000000..de3bd08c60d --- /dev/null +++ b/ACE/tests/Network_Adapters_Test.cpp @@ -0,0 +1,1176 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Network_Adapters_Test.cpp +// +// = DESCRIPTION +// Tests the ICMP-echo support in ACE. +// +// = AUTHOR +// Robert S. Iakobashvili <coroberti@gmail.com> <coroberti@walla.co.il> +// Gonzalo A. Diethelm <gonzalo.diethelm@aditiva.com> +// +// ============================================================================ + +// We need this to be able to check for ACE_HAS_ICMP_SUPPORT +#include "ace/config-all.h" +#include "test_config.h" + +#if defined (ACE_HAS_ICMP_SUPPORT) && (ACE_HAS_ICMP_SUPPORT == 1) + +#include "ace/ACE.h" +#include "ace/Get_Opt.h" +#include "ace/Signal.h" +#include "ace/High_Res_Timer.h" +#include "ace/Atomic_Op.h" +#include "ace/Sched_Params.h" +#include "ace/Reactor.h" +#include "ace/Timer_Queue.h" +#include "ace/OS_NS_string.h" + +#include "Network_Adapters_Test.h" + + +ACE_RCSID (tests, + Network_Adapters_Test, + "$Id$") + + +/** + * There are two major uses of the functionality: + * + * 1. to check a local network adapter; + * 2. to check which of the remote CEs (computer elements) are alive. + * + * For the first purpose we are creating a raw socket, binding it to + * the IP-address in question (adapter to be monitored), and are + * sending via the adapter ICMP echo-checks to a list of 3rd party + * ping-points. If at least a single 3rd party replies us within a + * configurable timeout by an ICMP-reply, our adapter is OK. If not, we + * may wish to repeat ICMP-probing once or twice more. We may also + * wish to make such tests regular with a configurable interval in seconds. + * + * For the second purpose we are creating a raw socket, and without + * binding it are sending via any our CE's adapter ICMP echo-checks to + * a list of CEs to be monitored. An array of chars (named ping_status + * in main ()), corresponding to the array of addresses + * (ping_points_addrs in main ()), contains status of each monitored + * CE. When we get ICMP-reply from a ping_points_addrs[I] IP-address, + * we are placing 0 to the ping_status[I]. The ICMP-probing may be + * configured to test 2-3 times each pinged CE. We may also wish to + * make such tests regular with a configurable interval in seconds. + * + * Command line options: + * + * -b IPv4 of the interface to bind to the socket (only for the + * purpose 1), e.g. -b 192.168.5.5; + * + * -p IPv4 addresses of the remote CEs, which we are going to check + * (purpose 2), or they are 3rd points for the purpose 1, + * e.g. “-p 192.168.5.120: 192.168.5.122: 192.168.5.125 + * + * -w milliseconds to wait for echo-reply, on lan 100-200 msec, on + * WAN may be 2000-5000 msec, for GPRS may reach 10000 - 20000 + * mseconds; + * + * -t as we are doing such checks regularly time in seconds between + * checks. + * + * In main we are activating by open () an instance of Echo_Handler + * with parameters. + * + * Repeats_Handler serves to repeat the checks each + * repeats_seconds_timer seconds. + * + * Stop_Handler contains a list of handlers to be stopped and is + * supposed to close this business. + + * Attention: Running the test without parameters (just using defaults) + * makes pinging to the loopback address. Therefore, the raw socket + * sees both ICMP_ECHO and ICMP_ECHOREPLY with the first output in log + * as not a ICMP_ECHOREPLY message and further ICMP_ECHOREPLY + * received. Don't worry, be happy - it's ok. + */ + + +Echo_Handler::Echo_Handler (void) + : ping_socket_ (), + reply_wait_ (), + remote_addrs_ (0), + number_remotes_ (0), + success_status_ (0), + delete_success_status_ (0), + max_attempts_num_ (0), + current_attempt_ (0), + connect_to_remote_ (0) +{ +} + +Echo_Handler::~Echo_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::~Echo_Handler - entered.\n"))); + + this->ping_socket ().close (); + if (this->remote_addrs_) + { + delete [] this->remote_addrs_; + this->remote_addrs_ = 0; + } + if (this->success_status_ && this->delete_success_status_) + { + delete this->success_status_; + } + this->success_status_ = 0; + + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::~Echo_Handler - completed.\n"))); +} + +int +Echo_Handler::open (ACE_Reactor * const reactor, + ACE_Time_Value const & reply_wait, + ACE_INET_Addr const & remote_addr, + ACE_TCHAR * success_status, + size_t max_attempts_num, + ACE_Addr const & local_addr, + int connect_to_remote) +{ + if (this->reactor ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("reactor is already set. \n")), + -1); + if (!reactor) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed : ") + ACE_TEXT ("NULL pointer to reactor provided. \n")), + -1); + + this->reactor (reactor); + this->reply_wait_ = reply_wait; + + if (this->remote_addrs_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("remote_addrs_ already initialized.\n")), + -1); + + ACE_NEW_RETURN (this->remote_addrs_, ACE_INET_Addr, -1); + + // now copy to keep it locally + this->remote_addrs_[0] = remote_addr; + this->number_remotes_ = 1; + if (this->success_status_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("success_status_ already initialized.\n")), + -1); + + if (! success_status) + { + ACE_NEW_RETURN (this->success_status_, ACE_TCHAR, -1); + this->delete_success_status_ = 1; + } + else + { + this->success_status_ = success_status; + } + + // place 'failed' to the array. + this->success_status_[0] = 1; + + this->max_attempts_num_ = max_attempts_num; + this->current_attempt_ = this->max_attempts_num_; + + if (this->ping_socket ().open (local_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open: %p\n"), + ACE_TEXT ("ping_socket_")), + -1); + + this->connect_to_remote_ = connect_to_remote; + + // Register with the reactor for input. + if (this->reactor ()->register_handler (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open: %p\n"), + ACE_TEXT ("register_handler for input")), + -1); + return 0; +} + +int +Echo_Handler::open (ACE_Reactor * const reactor, + ACE_Time_Value const & reply_wait, + ACE_INET_Addr const remote_addrs[], + size_t number_remotes, + ACE_TCHAR *success_status, + size_t max_attempts_num, + ACE_Addr const & local_addr) +{ + if (this->reactor ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("reactor is already set.\n")), + -1); + + //FUZZ: disable check_for_NULL + if (!reactor) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: NULL ") + ACE_TEXT ("pointer to reactor provided.\n")), + -1); + //FUZZ: enable check_for_NULL + + this->reactor (reactor); + this->reply_wait_ = reply_wait; + + if (!remote_addrs) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("NULL remote_addr pointer provided.\n")), + -1); + + if (!number_remotes) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("size of remote_addrs array is 0.\n")), + -1); + + this->number_remotes_ = number_remotes; + + if (this->remote_addrs_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("remote_addrs_ already initialized.\n")), + -1); + + ACE_NEW_RETURN (this->remote_addrs_, + ACE_INET_Addr[this->number_remotes_], + -1); + + // now copy to keep them locally + for (size_t i = 0; i < this->number_remotes_; ++i) + { + this->remote_addrs_[i] = remote_addrs[i]; + } + + if (this->success_status_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open - failed: ") + ACE_TEXT ("success_status_ already initialized.\n")), + -1); + + if (! success_status) + { + ACE_NEW_RETURN (this->success_status_, + ACE_TCHAR[this->number_remotes_], + -1); + this->delete_success_status_ = 1; + } + else + { + this->success_status_ = success_status; + } + + // place 'failed' to the this->success_status_ array. + for (size_t j = 0; j < this->number_remotes_; ++j) + { + this->success_status_[j] = 1; + } + + this->max_attempts_num_ = max_attempts_num; + this->current_attempt_ = this->max_attempts_num_; + + if (this->ping_socket ().open (local_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open: %p\n"), + ACE_TEXT ("ping_socket_")), + -1); + + // register with the reactor for input + if (this->reactor ()->register_handler (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::open: %p\n"), + ACE_TEXT ("register_handler for input")), + -1); + return 0; +} + +ACE_Ping_Socket & +Echo_Handler::ping_socket (void) +{ + return this->ping_socket_; +} + +int +Echo_Handler::dispatch_echo_checks (int first_call) +{ + // Set ones , if this is the first call (not from handle_timeout) + if (first_call) + { + for (size_t j = 0; j < this->number_remotes_; ++j) + { + this->success_status_[j] = 1; + } + this->current_attempt_ = this->max_attempts_num_; + } + + // Send echo-checks. + for (size_t i = 0; i < this->number_remotes_; ++i) + { + if (this->success_status_[i] != 0) + { + if (this->ping_socket ().send_echo_check ( + this->remote_addrs_[i], + this->connect_to_remote_) == -1) + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::dispatch_echo_checks - ") + ACE_TEXT ("failed for this->remote_addrs_[%d].\n"), + i)); + } + } + + int rval_sched = -1; + if ((rval_sched = + this->reactor ()->schedule_timer (this, + 0, + ACE_Time_Value (1), + this->reply_wait_)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::dispatch_echo_checks:") + ACE_TEXT (" %p\n"), + ACE_TEXT ("schedule_timer")), + -1); + return 0; +} + +int +Echo_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_close - started.\n"))); + +#if 0 + this->ping_socket ().close (); +#endif + + this->reactor ()->cancel_timer (this); + +#if 0 + this->reactor ()->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); +#endif + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_close - completed.\n"))); + return 0; +} + +ACE_HANDLE +Echo_Handler::get_handle (void) const +{ + return ((ACE_ICMP_Socket &) this->ping_socket_).get_handle (); +} + +int +Echo_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_input - ") + ACE_TEXT ("activity occurred on handle %d!\n"), + this->ping_socket ().get_handle ())); + + ACE_TCHAR buf[BUFSIZ]; + ACE_OS::memset (buf, 0, sizeof buf); + + ACE_INET_Addr addr; + int rval_recv = -1; + + // Receive an <n> byte <buf> from the datagram socket + // (uses<recvfrom(3)>). + rval_recv = + this->ping_socket ().recv (this->ping_socket ().icmp_recv_buff (), + ACE_Ping_Socket::PING_BUFFER_SIZE, + addr); + switch (rval_recv) + { + case -1: + // Complain and leave, but keep registered, returning 0. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_input - ") + ACE_TEXT ("%p: bad read\n"), + ACE_TEXT ("client")), + 0); + // NOTREACHED + + case 0: + // Complain and leave + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_input - ") + ACE_TEXT ("closing daemon (fd = %d)\n"), + this->get_handle ()), + 0); + // NOTREACHED + + default: + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_input - ") + ACE_TEXT ("message from %d bytes received.\n"), + rval_recv)); + + if (! this->ping_socket ().process_incoming_dgram ( + this->ping_socket ().icmp_recv_buff (), + rval_recv)) + { + for (size_t k = 0; k <this->number_remotes_; ++k) + { + if (addr.get_ip_address () == + this->remote_addrs_[k].get_ip_address ()) + { + if (addr.addr_to_string (buf, sizeof buf) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("can't obtain peer's address"))); + } + else + { + ACE_DEBUG + ((LM_INFO, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_input - ") + ACE_TEXT ("ECHO_REPLY received ") + ACE_TEXT ("from %s; marking this peer alive\n"), + buf)); + } + // mark as successful + this->success_status_[k] = 0; + break; + } + } + } + break; + } + + return 0; +} + +int +Echo_Handler::handle_timeout (ACE_Time_Value const &, + void const *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_timeout - ") + ACE_TEXT ("timer for ping_socket_ with handle %d.\n"), + this->ping_socket ().get_handle ())); + + int need_to_proceed = 0; + + for (size_t i = 0; i < this->number_remotes_; ++i) + { + if (this->success_status_[i]) + { + need_to_proceed = 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_timeout - ") + ACE_TEXT ("this->success_status_[%d] is not zero. ") + ACE_TEXT ("Need to proceed echo-checks.\n"), i)); + break; + } + } + + if (!need_to_proceed) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_timeout - ") + ACE_TEXT ("need_to_proceed == 0. ") + ACE_TEXT ("Completed echo-checks.\n"))); + } + + if (!this->current_attempt_ || !need_to_proceed) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_timeout - ") + ACE_TEXT ("completed ECHO-checks for handle (%d).\n"), + this->ping_socket ().get_handle ())); + return -1; // to de-register from Reactor and make clean-up + // in handle-close + } + + if (this->current_attempt_) + { + --this->current_attempt_; + } + + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echo_Handler::handle_timeout - attempt %d.\n"), + this->current_attempt_)); + + this->dispatch_echo_checks (); + return 0; +} + +int +Echo_Handler::does_echo_test_successful (void) +{ + for (size_t i = 0; i < this->number_remotes_; ++i) + { + if (!this->success_status_[i]) + { + return 1; + } + } + return 0; +} + + +Stop_Handler::Stop_Handler (ACE_Reactor * const reactor) + : counter_ ((counter_sig) 1) +{ + this->reactor (reactor); + ACE_OS::memset (this->handlers_to_stop_, + 0, + sizeof this->handlers_to_stop_); +} + +Stop_Handler::~Stop_Handler (void) +{ + ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) Stop_Handler::~Stop_Handler.\n"))); +} + +int +Stop_Handler::open (void) +{ + // Register the signal handler object to catch the signals. + if (this->reactor ()->register_handler (SIGINT, this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::open: %p\n"), + ACE_TEXT ("register_handler for SIGINT")), + -1); + + if (this->reactor ()->register_handler (SIGTERM, this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::open: %p\n"), + ACE_TEXT ("register_handler for SIGTERM")), + -1); + +#if ! defined (ACE_WIN32) + if (this->reactor ()->register_handler (SIGQUIT, this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::open: %p\n"), + ACE_TEXT ("register_handler for SIGQUIT")), + -1); +#endif /* #if ! defined (ACE_WIN32) */ + return 0; +} + +int +Stop_Handler::handle_signal (int signum, + siginfo_t * , + ucontext_t *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Stop_Handler::handle_signal - started.\n"))); + if (! --this->counter_) + { + ACE_DEBUG ((LM_INFO, ACE_TEXT ("\n-- Stop_Handler::handle_signal --- ") + ACE_TEXT ("SIGNAL %d RECEIVED -----------.\n"), + signum)); + return reactor ()->notify (this, ACE_Event_Handler::READ_MASK); + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Stop_Handler::handle_signal - ") + ACE_TEXT ("finished.\n"))); + return 0; +} + +int +Stop_Handler::handle_input (ACE_HANDLE handle) +{ + ACE_UNUSED_ARG (handle); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) Stop_Handler::handle_input - entered\n"))); + + for (size_t i = 0; i < HANDLERS_TO_STOP_TABLE_SIZE; ++i) + { + // remove from the reactor's tables all non-null entries + if (this->handlers_to_stop_[i]) + { +#if defined ACE_HAS_EXCEPTIONS + + // protect from deleted pointer + try + { +#endif // ACE_HAS_EXCEPTIONS + + this->reactor ()->cancel_timer (this->handlers_to_stop_[i]); + this->reactor ()->remove_handler + (this->handlers_to_stop_[i], + ACE_Event_Handler::ALL_EVENTS_MASK + | ACE_Event_Handler::DONT_CALL); +#if defined ACE_HAS_EXCEPTIONS + } + catch (...) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::handle_input - ") + ACE_TEXT ("EXCEPTION CATCHED. Most probably ") + ACE_TEXT ("handler's pointer has been deleted.\n"))); + } +#endif // ACE_HAS_EXCEPTIONS + this->handlers_to_stop_[i] = 0; + } + } + + this->reactor ()->remove_handler (this, + ACE_Event_Handler::SIGNAL_MASK | + ACE_Event_Handler::DONT_CALL); + + if (reactor ()->end_reactor_event_loop () == -1) + { + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Stop_Handler::handle_signal:%p\n"), + ACE_TEXT ("end_reactor_event_loop")), + -1); + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) Stop_Handler::handle_input - completed.\n"))); + return 0; +} + +int +Stop_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) Stop_Handler::handle_close - entered.\n"))); + this->reactor ()->remove_handler (this, + ACE_Event_Handler::SIGNAL_MASK | + ACE_Event_Handler::DONT_CALL); + + if (reactor ()->end_reactor_event_loop () == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("Stop_Handler::handle_close: %p\n"), + ACE_TEXT ("end_reactor_event_loop")), + -1); + return 0; +} + +int +Stop_Handler::handle_timeout (ACE_Time_Value const &, + void const *) +{ + return 0; +} + +// Register handler with us for stopping. +int +Stop_Handler::register_handler (ACE_Event_Handler *handler) +{ + if (!handler) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::register_handler - ") + ACE_TEXT ("error, handler is a null pointer.\n")), + -1); + + size_t index = 0; + + for (index = 0; + (index < HANDLERS_TO_STOP_TABLE_SIZE && + this->handlers_to_stop_[index]); + ++index) + ; + + if (index == HANDLERS_TO_STOP_TABLE_SIZE) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::register_handler ") + ACE_TEXT ("- error, no space in ") + ACE_TEXT ("handlers_to_stop_table.\nIncrease ") + ACE_TEXT ("HANDLERS_TO_STOP_TABLE_SIZE.\n")), + -1); + } + + this->handlers_to_stop_[index] = handler; + return 0; +} + +// Unregister handler, registered before with us for stopping. +int +Stop_Handler::unregister_handler (ACE_Event_Handler *handler) +{ + if (!handler) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::unregister_handler - ") + ACE_TEXT ("error, handler is a null pointer.\n")), + -1); + + size_t index = 0; + + for (index = 0; + (index < HANDLERS_TO_STOP_TABLE_SIZE && + handler != this->handlers_to_stop_[index]); + ++index) + ; + + size_t entrance = 0; + if (index == HANDLERS_TO_STOP_TABLE_SIZE) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::unregister_") + ACE_TEXT ("handler - error, the handler was not ") + ACE_TEXT ("found among registered handlers.\n")), + -1); + + entrance = index; + // null the entrance. Nulled entrances cannot be destroyed + this->handlers_to_stop_[entrance] = 0; + + return 0; +} + + +Repeats_Handler::Repeats_Handler (void) + : check_handler_ (0), + seconds_timer_ (60), + counter_ (0) +{ +} + +Repeats_Handler::~Repeats_Handler (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) Repeats_Handler::~Repeats_Handler.\n"))); +} + +int +Repeats_Handler::open (Echo_Handler * check_handler, + ACE_Reactor * const reactor, + unsigned int seconds_timer) +{ + if (!check_handler) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Repeats_Handler::open - error: ") + ACE_TEXT ("NULL check_handler.\n")), + -1); + + this->check_handler_ = check_handler; + + if (!reactor) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Stop_Handler::open - error: ") + ACE_TEXT ("NULL reactor.\n")), + -1); + + this->reactor (reactor); + this->seconds_timer_ = seconds_timer; + + if (this->reactor ()->schedule_timer ( + this, + 0, + ACE_Time_Value (1), + ACE_Time_Value (this->seconds_timer_)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Repeats_Handler::open: %p\n"), + ACE_TEXT ("schedule_timer")), + -1); + return 0; +} + +int +Repeats_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG + ((LM_INFO, + ACE_TEXT ("(%P|%t) Repeats_Handler::handle_close - entered.\n"))); + + this->reactor ()->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); + return 0; +} + +static int one_button_test = 0; + +int +Repeats_Handler::handle_timeout (ACE_Time_Value const &, + void const *) +{ + this->counter_++ ; + if (one_button_test && this->counter_ > 3) + { + ::raise (SIGINT); + } + if (this->check_handler_) + { + return this->check_handler_->dispatch_echo_checks (true); + } + + return -1; +} + +// to create core on some UNIX platforms +#if defined (ACE_HAS_SIG_C_FUNC) +extern "C" +{ +#endif /* #if defined (ACE_HAS_SIG_C_FUNC) */ + +#if ! defined (ACE_WIN32) + static void sigsegv_handler (int) + { + ACE_OS::abort (); + } +#endif /* #if ! defined (ACE_WIN32) */ + +#if defined (ACE_HAS_SIG_C_FUNC) +} +#endif /* #if defined (ACE_HAS_SIG_C_FUNC) */ + +#if defined (ACE_WIN32) +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_SHUTDOWN_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + ::raise (SIGINT); + return TRUE; + + // Pass other signals to the next handler. + default: + return FALSE; + } +} +#endif /* #if defined (ACE_WIN32) */ + + +Fini_Guard::Fini_Guard (void) +{ +} + +Fini_Guard::~Fini_Guard (void) +{ + ACE::fini (); +} + + +#define MAX_NUMBER_OF_PING_POINTS 16 + +static int number_of_ping_points = 0; +static char ping_points_ips [MAX_NUMBER_OF_PING_POINTS][16]; +static ACE_INET_Addr ping_points_addrs [MAX_NUMBER_OF_PING_POINTS]; +static ACE_TCHAR local_ip_to_bind [16]; + +static int wait_echo_reply_timer = 500; // 500 ms to wait is the default +static int repeats_seconds_timer = 60; // 60 seconds between repeats + +static int +is_ip_address_local (char const * const ip_to_bind) +{ + ACE_INET_Addr *the_addr_array; + size_t how_many = 0; + int rc = ACE::get_ip_interfaces (how_many, the_addr_array); + + if (rc != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("is_ip_address_local: %p\n"), + ACE_TEXT ("ACE::get_ip_interfaces")), + -1); + + if (how_many == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("is_ip_address_local: "), + ACE_TEXT ("No interfaces presently configured ") + ACE_TEXT ("in the kernel\n")), + -1); + + // debugging messages + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("is_ip_address_local () - there are %d interfaces\n"), + how_many)); + + for (size_t i = 0; i < how_many; ++i) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\t%s\n"), + the_addr_array[i].get_host_addr ())); + } + + for (size_t j = 0; j < how_many; ++j) + { + if (!ACE_OS::strcmp (the_addr_array[j].get_host_addr (), ip_to_bind)) + { + return 0; + } + } + return -1; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_OS::memset (ping_points_ips, 0, sizeof ping_points_ips); + ACE_OS::memset (local_ip_to_bind, 0, sizeof local_ip_to_bind); + + if (argc == 1) // one button test + { + one_button_test = 1; + repeats_seconds_timer = 2; + number_of_ping_points = 1; + + ACE_OS::strncpy (ping_points_ips [0], + "127.0.0.1", + sizeof ping_points_ips [0]); + + ping_points_addrs[0].set ((u_short) 0, ping_points_ips[0]); + return 0; + } + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("b:p:t:w:")); + int c, counter = 0; + ACE_INET_Addr b_temp_addr; + ACE_TCHAR *token = 0; + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'b': // ip-address of the interface to bind to + ACE_OS::strncpy (local_ip_to_bind, + get_opt.optarg, + sizeof local_ip_to_bind); + + if (!ACE_OS::strlen (local_ip_to_bind) || + b_temp_addr.set ((u_short)0, local_ip_to_bind) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("-b should be followed by a valid ") + ACE_TEXT ("IPv4 address.\n"))); + // print_usage (); + return -1; + } + if (is_ip_address_local (local_ip_to_bind) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("the -b address (%s) ") + ACE_TEXT ("is not a local ") + ACE_TEXT ("address of your computer.\n") + ACE_TEXT ("\tPlease correct it.\n"), + local_ip_to_bind), + -1); + } + break; + + case 'p': // ping-point (target) ip-addresses, separated by ":'" + + // tokenizing the string + for (token = ACE_OS::strtok (get_opt.optarg, ACE_TEXT (":")); + token != 0 && counter < MAX_NUMBER_OF_PING_POINTS; + token = ACE_OS::strtok (0, ACE_TEXT (":"))) + { + if (ping_points_addrs[counter].set ((u_short)0, token) != 0) + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("Error: the address \"%s\" is not ") + ACE_TEXT ("a valid IPv4 address.\n"), + token), + -1); + ++number_of_ping_points; + ++counter; + } + break; + + case 't': + repeats_seconds_timer = ACE_OS::atoi (get_opt.optarg); + break; + + case 'w': + wait_echo_reply_timer = ACE_OS::atoi (get_opt.optarg); + break; + + default: + // return print_usage (argc,argv); + break; + + } + } + + if (!number_of_ping_points) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Error: no valid IPv4 addresses ") + ACE_TEXT ("were provided, using -p option.\n")), + -1); + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Network_Adapters_Test")); + + ACE::init (); + + // to call for ACE::fini () in its destructor + Fini_Guard fg; + +#if defined (ACE_WIN32) + SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE); +#else /* #if defined (ACE_WIN32) */ + // Set a handler for SIGSEGV signal to call for abort. + ACE_Sig_Action sa1 ((ACE_SignalHandler) sigsegv_handler, SIGSEGV); +#endif /* #if defined (ACE_WIN32) */ + if (::parse_args (argc, argv) == -1) + { + return -1; + } + + ACE_Reactor * main_reactor = 0; + ACE_NEW_RETURN (main_reactor, ACE_Reactor, -1); + + (void) ACE_High_Res_Timer::global_scale_factor (); + main_reactor->timer_queue ()->gettimeofday + (&ACE_High_Res_Timer::gettimeofday_hr); + + /** + * Stop_Handler's is supposed to stop the activity of all + * handlers by a SIGINT signal. We create and activate here an object of + * Stop_Handler and pass an instance of reactor (main_reactor), + * running demultiplexing event loop in the "main thread". + */ + Stop_Handler* stop_handler = 0; + ACE_NEW_RETURN (stop_handler, Stop_Handler (main_reactor), -1); + if (stop_handler->open () == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("main() - stop_handler->open"))); + ACE_OS::exit(-2); + } + + ACE_TCHAR *ping_status = 0; + ACE_NEW_RETURN (ping_status, ACE_TCHAR[number_of_ping_points], -1); + + // wait_echo_reply_timer is in msec + int seconds = 0; + int milliseconds = 0; + seconds = wait_echo_reply_timer / 1000; + milliseconds = wait_echo_reply_timer % 1000; + ACE_Time_Value const wait_timer (seconds, milliseconds); + + Echo_Handler *ping_handler; + ACE_NEW_RETURN (ping_handler, Echo_Handler, -1); + + if (ACE_OS::strlen (local_ip_to_bind)) + { + // We are willing to bind the raw-socket to a certain adapter, + // probably because we are willing to check connectivity/etc + // of the local adapter. + ACE_INET_Addr local_adapter; + local_adapter.set ((u_short) 0, local_ip_to_bind); + if (ping_handler->open (main_reactor, + wait_timer, + ping_points_addrs, + number_of_ping_points, + ping_status, + 2, // max_attempts_number + local_adapter) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("main() - ping_handler->open"))); + ACE_OS::exit (-4); + } + } + else + { + // Binding to a local adapter is not of our interest. We just + // are willing to check all these remote IPs, to monitor, that + // they are alive. + if (ping_handler->open (main_reactor, + wait_timer, + ping_points_addrs, + number_of_ping_points, + ping_status, + 2) == -1) // max_attempts_number + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("main() - ping_handler->open ()"))); + ACE_OS::exit (-4); + } + } + + Repeats_Handler *repeats_handler; + ACE_NEW_RETURN (repeats_handler, Repeats_Handler, -1); + if (repeats_handler->open (ping_handler, + main_reactor, + repeats_seconds_timer) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("main() - repeats_handler->open"))); + ACE_OS::exit (-4); + } + + stop_handler->register_handler (repeats_handler); + stop_handler->register_handler (ping_handler); + + // Demultiplexing event loop of the main_reactor. + while (main_reactor->reactor_event_loop_done () == 0) + { + main_reactor->run_reactor_event_loop (); + } + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t|%T) \"Network_Adapters_Test\" main() - ") + ACE_TEXT ("out of reactor's loop.\n"))); + + delete repeats_handler; + delete ping_handler; + delete [] ping_status; + delete stop_handler; + delete main_reactor; + + ACE_END_TEST; + return 0; +} + +#else + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Network_Adapters_Test")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t|%T) \"Network_Adapters_Test\" main() - ") + ACE_TEXT ("ICMP support not configured.\n") + ACE_TEXT ("Define ACE_HAS_ICMP_SUPPORT = 1 in your config.h ") + ACE_TEXT ("file to enable.\n"))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_ICMP_SUPPORT == 1 */ diff --git a/ACE/tests/Network_Adapters_Test.h b/ACE/tests/Network_Adapters_Test.h new file mode 100644 index 00000000000..c49cb40f03d --- /dev/null +++ b/ACE/tests/Network_Adapters_Test.h @@ -0,0 +1,272 @@ +// -*- C++ -*- + +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Network_Adapters_Test.h +// +// = DESCRIPTION +// Definitions for Network_Adapters_Test.cpp. +// +// = AUTHOR +// Robert S. Iakobashvili <roberti@go-WLAN.com> <coroberti@walla.co.il> +// Gonzalo A. Diethelm <gonzalo.diethelm@aditiva.com> made aceing +// +// ============================================================================ + +#ifndef ACE_NETWORK_ADAPTERS_TEST_H +#define ACE_NETWORK_ADAPTERS_TEST_H + +#include "ace/Ping_Socket.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_HAS_ICMP_SUPPORT) && (ACE_HAS_ICMP_SUPPORT == 1) + +#include "ace/Reactor.h" +#include "ace/INET_Addr.h" +#include "ace/Event_Handler.h" +#include "ace/Mutex.h" + +/* + * Echo_Handler performs echo-checks against a single ICMP echo-point. + */ +class Echo_Handler : public ACE_Event_Handler +{ +public: + // = Initialization and termination methods. + + /// Default constructor + Echo_Handler (void); + + /// Destructor + virtual ~Echo_Handler (void); + + /** + * Initialization of a handler, performing echo-checks against a + * SINGLE echo-point (supposed to be a really reliable, like hub, + * router). + * + * <reactor> - to be used for demultiplexing of any input and + * timeout + * <reply_wait> - time to wait for reply + * <remote_addr> - pointer to the remote address to sent to ICMP + * ECHO_CHECK datagram + * <success_status> - a pointer to char to be set as a 0 - on + * success, and 1 - when failed + * <max_attempts_num> - maximum number of attempts to perform + * <local_addr> - the local address to bind the underlaying + * ACE::Ping_Socket; useful for checks of local network adapters + * <connect_to_remote> - whether to connect the underlaying + * ACE::Ping_Socket to the remote address (1), or not (0) + */ + int open (ACE_Reactor * const reactor, + const ACE_Time_Value & reply_wait, + const ACE_INET_Addr & remote_addr, + ACE_TCHAR * success_status = 0, + size_t max_attempts_num = 1, + const ACE_Addr & local_addr = ACE_Addr::sap_any, + int connect_to_remote = 0); + + /** + * Initialization of a handler, performing echo-checks against + * MULTIPLE echo-points. + * + * <reactor> - to be used for demultiplexing of any input and + * timeout; + * <reply_wait> - time to wait for reply; + * <remote_addrs> - an array of remote addresses to sent to ICMP + * ECHO_CHECK datagram; + * <success_status> - an array of chars, each of them representing + * a respective remote address to be set as a 0 - on success, and + * 1 - when failed; + * <max_attempts_num> - maximum number of attempts to perform; + * <local_addr> - the local address to bind the underlaying + * ACE::Ping_Socket; useful for checks of the local network adapters + * connectivity; + */ + int open (ACE_Reactor * const reactor, + ACE_Time_Value const & reply_wait, + ACE_INET_Addr const remote_addrs[], + size_t number_remotes, + ACE_TCHAR * success_status = 0, + size_t max_attempts_num = 1, + ACE_Addr const & local_addr = ACE_Addr::sap_any); + + /// Returns reference to the ACE::Ping_Socket. Necessary for ACE_Reactor. + virtual ACE_HANDLE get_handle (void) const; + + /** + * Takes care of the input. Reads the incoming ICMP datagrams and + * calls for process_incoming () of the ping_socket for processing. + */ + virtual int handle_input (ACE_HANDLE handle); + + /* + * Decides, if we need to continue checks (when at least a single + * address not returned ICMP_ECHO_REPLY and number of attempts, + * set in open () not expired). If yes, calls for dispatch_echo_checks (), + * if not returns -1 to initiate clean-up. + */ + virtual int handle_timeout (ACE_Time_Value const & tv, + void const * arg = 0); + + /// Makes clean-up + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + /// Calls send_echo_check() for all remote_addrs_ + int dispatch_echo_checks (int first_call = 0); + + /// Access to ping_socket. + ACE_Ping_Socket& ping_socket (void); + + /// Returns 1 if a single char of success_status_ is 0 + /// (connected). + int does_echo_test_successful (void); + +private: + + /// Wrapper for sending/receiving ICMPs. + ACE_Ping_Socket ping_socket_; + + /// Time to wait for reply. + ACE_Time_Value reply_wait_; + + /// Remote address to ping on it + ACE_INET_Addr *remote_addrs_; + + /// Number of remote echo points + size_t number_remotes_; + + /// When 0 - success, 1 - failed. + ACE_TCHAR *success_status_; + + /// If 1 - we 'own' + int delete_success_status_; + + /// Maximum number of attempts. + size_t max_attempts_num_; + + /// The number of the current attempt. + size_t current_attempt_; + + /// Whether to make connect to the remote address or not. May be + /// buggy on some platforms. + int connect_to_remote_; +}; + + +/* + * Class Stop_Handler - the most important class of the process. + * Knows how to stop all this business. + */ +class Stop_Handler : public ACE_Event_Handler +{ +public: + typedef ACE_Atomic_Op<ACE_Mutex, long> counter_sig; + + // Constructor. + Stop_Handler (ACE_Reactor * const reactor = ACE_Reactor::instance ()); + + // Destructor. + virtual ~Stop_Handler (void); + + // Initialization. Registers this for SIGINT, SIGTERM and SIGQUIT. + virtual int open (void); + + // De-registers this from the reactor and stops reactors event_loop. + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + // Called by reactor from the notification queue. + virtual int handle_input (ACE_HANDLE); + + // Dispatches handle_input () notification. + virtual int handle_signal (int signum, + siginfo_t * = 0, + ucontext_t * = 0); + + virtual int handle_timeout (ACE_Time_Value const & current_time, + void const * act = 0); + + // Register handler with us for stopping. + virtual int register_handler (ACE_Event_Handler * handler); + + // Unregister handler, registered before with us for stopping. + virtual int unregister_handler (ACE_Event_Handler * handler); + +private: + + enum + { + HANDLERS_TO_STOP_TABLE_SIZE = 10 + }; + + // Flag to prevent multiple dispatching of handle_input (). + counter_sig counter_; + + // Table to place here pointers to all tasks in the process. + ACE_Event_Handler * handlers_to_stop_[HANDLERS_TO_STOP_TABLE_SIZE]; +}; + + +/* + * TODO comment + */ +class Repeats_Handler : public ACE_Event_Handler +{ +public: + // Constructor. + Repeats_Handler (void); + + // Destructor. + virtual ~Repeats_Handler (void); + + // Initialization. + virtual int open (Echo_Handler *check_handler, + ACE_Reactor * const reactor = ACE_Reactor::instance (), + unsigned int seconds_timer = 60); + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + // dispatches a new echo-checks series + virtual int handle_timeout (ACE_Time_Value const & current_time, + void const * act = 0); + +private: + // an instance of a handler + Echo_Handler * check_handler_; + + // timer in seconds to repeat the checks + unsigned int seconds_timer_; + + // counts repeats + unsigned long counter_; +}; + + +/* + * TODO comment + */ +class Fini_Guard +{ +public: + // Constructor + Fini_Guard (void); + + // Destructor - calls for fini + ~Fini_Guard (void); +}; + +#endif /* ACE_HAS_ICMP_SUPPORT == 1 */ + +#endif /* ACE_NETWORK_ADAPTERS_TEST_H */ diff --git a/ACE/tests/New_Fail_Test.cpp b/ACE/tests/New_Fail_Test.cpp new file mode 100644 index 00000000000..8b8581dd136 --- /dev/null +++ b/ACE/tests/New_Fail_Test.cpp @@ -0,0 +1,223 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// New_Fail_Test.cpp +// +// = DESCRIPTION +// Checks to be sure that a failed ACE_NEW[_RETURN | _NORETURN] doesn't end +// up throwing an exception up to the caller. +// +// Note that this test doesn't get a real attempt on platforms which: +// 1. Are known to throw exceptions when 'new' runs out of resources, +// 2. Are built with exceptions disabled. +// In these cases, the test puts a message in the log noting that a failed +// new will throw an exception, and trust that the user accepts that risk. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Log_Msg.h" +#include "ace/OS_Memory.h" +#include "ace/CORBA_macros.h" + +ACE_RCSID(tests, New_Fail_Test, "$Id$") + +#if (!defined (__SUNPRO_CC) && !defined (__GNUG__)) || \ + defined (ACE_HAS_EXCEPTIONS) + +#include "ace/Numeric_Limits.h" + +// This test allocates all of the heap memory, forcing 'new' to fail +// because of a lack of memory. The ACE_NEW macros should prevent an +// exception from being thrown past the ACE_NEW. If this test doesn't +// wipe out on an alloc exception, it passes. +// +// If it doesn't ever fail an allocation, there's a warning that something is +// wrong. The allocated memory is always freed to avoid masking a leak +// somewhere else in the test. + +// Most we can do, by half. Using max alone gets "invalid allocation size" +// messages on stdout on Windows. +static const size_t BIG_BLOCK = ACE_Numeric_Limits<size_t>::max () / 2; + +// Shouldn't take many "as much as possible" tries to get a failure. +static const int MAX_ALLOCS_IN_TEST = 4; + +static void +try_ace_new (char **p) +{ + ACE_NEW (*p, char[BIG_BLOCK]); + return; +} + +static char * +try_ace_new_return (void) +{ + char *p = 0; + ACE_NEW_RETURN (p, char[BIG_BLOCK], 0); + return p; +} + +static char * +try_ace_new_noreturn (void) +{ + char *p = 0; + ACE_NEW_NORETURN (p, char[BIG_BLOCK]); + return p; +} +#endif /* (!__SUNPRO_CC && !__GNUG__) || ACE_HAS_EXCEPTIONS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("New_Fail_Test")); + int status = 0; + + // Some platforms are known to throw an exception on a failed 'new', + // but are customarily built without exception support to improve + // performance. These platforms are noted, and the test passes. + // For new ports, it is wise to let this test run. Depending on + // intended conditions, exceptions can be disabled when the port is + // complete. +#if (defined (__SUNPRO_CC) || defined (__GNUG__)) && \ + !defined (ACE_HAS_EXCEPTIONS) + ACE_DEBUG ((LM_NOTICE, ACE_TEXT ("Out-of-memory will throw an unhandled exception\n"))); + ACE_DEBUG ((LM_NOTICE, ACE_TEXT ("Rebuild with exceptions=1 to prevent this, but it may impair performance.\n"))); + +#else + + char *blocks[MAX_ALLOCS_IN_TEST]; + int i; + +# if defined (ACE_HAS_EXCEPTIONS) + try + { +# endif /* ACE_HAS_EXCEPTIONS */ + // First part: test ACE_NEW + for (i = 0; i < MAX_ALLOCS_IN_TEST; i++) + { + try_ace_new (&blocks[i]); + if (blocks[i] == 0) + break; + } + if (i == MAX_ALLOCS_IN_TEST) + { + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("Test didn't exhaust available memory\n"))); + // Back up to valid pointer for deleting. + --i; + } + else + { + ACE_ASSERT (blocks[i] == 0); + if (errno == ENOMEM) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_NEW failed properly at block %d\n"), + i)); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_NEW failed at block %d, but ") + ACE_TEXT ("expected ENOMEM, %p (%d)\n"), + i, + ACE_TEXT ("got"), + errno)); + } + + // Free the memory to try ACE_NEW_RETURN + while (i >= 0) + delete [] blocks[i--]; + + // Second part: test ACE_NEW_RETURN + for (i = 0; i < MAX_ALLOCS_IN_TEST; i++) + { + blocks[i] = try_ace_new_return (); + if (blocks[i] == 0) + break; + } + + if (i == MAX_ALLOCS_IN_TEST) + { + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("Test didn't exhaust available memory\n"))); + // Back up to valid pointer. + --i; + } + else + { + ACE_ASSERT (blocks[i] == 0); + if (errno == ENOMEM) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_NEW_RETURN failed properly at block %d\n"), + i)); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_NEW_RETURN failed at block %d, but ") + ACE_TEXT ("expected ENOMEM, %p (%d)\n"), + i, + ACE_TEXT ("got"), + errno)); + } + while (i >= 0) + delete [] blocks[i--]; + + // Third part: test ACE_NEW_NORETURN + for (i = 0; i < MAX_ALLOCS_IN_TEST; i++) + { + blocks[i] = try_ace_new_noreturn (); + if (blocks[i] == 0) + break; + } + + if (i == MAX_ALLOCS_IN_TEST) + { + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("Test didn't exhaust available memory\n"))); + // Back up to valid pointer. + --i; + } + else + { + ACE_ASSERT (blocks[i] == 0); + if (errno == ENOMEM) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("ACE_NEW_NORETURN failed properly at block %d\n"), + i)); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_NEW_NORETURN failed at block %d, but ") + ACE_TEXT ("expected ENOMEM, %p (%d)\n"), + i, + ACE_TEXT ("got"), + errno)); + } + while (i >= 0) + delete [] blocks[i--]; + +# if defined (ACE_HAS_EXCEPTIONS) + } + + catch (...) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Caught exception during test; ") + ACE_TEXT ("ACE_bad_alloc not defined correctly, or\n"))); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_NEW_THROWS_EXCEPTIONS is not #defined ") + ACE_TEXT ("(and should be).\n"))); + // Mark test failure + status = 1; + } +# endif /* ACE_HAS_EXCEPTIONS */ +#endif /* __SUNPRO_CC && !ACE_HAS_EXCEPTIONS */ + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/NonBlocking_Conn_Test.cpp b/ACE/tests/NonBlocking_Conn_Test.cpp new file mode 100644 index 00000000000..80e25c63cfb --- /dev/null +++ b/ACE/tests/NonBlocking_Conn_Test.cpp @@ -0,0 +1,319 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// NonBlocking_Conn_Test.cpp +// +// = DESCRIPTION +// This test checks for the proper working of the following: +// - blocking connections +// - blocking connections with timeouts +// - non-blocking connections +// - non-blocking connections without waiting for completions +// - non-blocking connections with timeouts +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "test_config.h" +#include "NonBlocking_Conn_Test.h" +#include "ace/Connector.h" +#include "ace/SOCK_Connector.h" +#include "ace/Select_Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Get_Opt.h" + +static int test_select_reactor = 1; +static int test_tp_reactor = 1; +static int test_wfmo_reactor = 1; + +Svc_Handler::Svc_Handler (void) + : status_ (0), + completion_counter_ (0) +{ +} + +void +Svc_Handler::connection_status (Connection_Status &status, + int &completion_counter) +{ + this->status_ = &status; + this->completion_counter_ = &completion_counter; +} + +int +Svc_Handler::open (void *) +{ + *this->status_ = SUCCEEDED; + (*this->completion_counter_)++; + + return 0; +} + +int +Svc_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask mask) +{ + *this->status_ = FAILED; + (*this->completion_counter_)++; + + return ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>::handle_close (handle, + mask); +} + +typedef ACE_Connector<Svc_Handler, + ACE_SOCK_CONNECTOR> + CONNECTOR; + +static const char* hosts[] = { + "www.russiantvguide.com:80", + "www.pakarmy.gov.pk:80", + "www.cnn.com:80", + "www.waca.com.au:80", + "www.uganda.co.ug:80", + "www.cs.wustl.edu:80", + "www.dre.vanderbilt.edu:80", + "www.dhm.gov.np:80", + "www.msn.com:80", + "www.presidencymaldives.gov.mv:80" }; + +static int number_of_connections = 0; + +void +test_connect (ACE_Reactor &reactor, + ACE_INET_Addr *addresses, + ACE_Synch_Options &synch_options, + int complete_nonblocking_connections) +{ + CONNECTOR connector (&reactor); + + int i = 0; + + int completion_counter = 0; + Svc_Handler::Connection_Status *connection_status = + new Svc_Handler::Connection_Status[number_of_connections]; + + Svc_Handler **svc_handlers = + new Svc_Handler *[number_of_connections]; + + for (i = 0; i < number_of_connections; ++i) + { + svc_handlers[i] = + new Svc_Handler; + + svc_handlers[i]->connection_status (connection_status[i], + completion_counter); + } + + connector.connect_n (number_of_connections, + svc_handlers, + addresses, + 0, + synch_options); + + if (!synch_options[ACE_Synch_Options::USE_REACTOR]) + ACE_ASSERT (completion_counter == number_of_connections); + + if (complete_nonblocking_connections) + { + while (completion_counter != number_of_connections) + { + connector.reactor ()->handle_events (); + } + } + + connector.close (); + + for (i = 0; i < number_of_connections; ++i) + { + ACE_TCHAR buffer[1024]; + addresses[i].addr_to_string (buffer, + sizeof buffer / sizeof (ACE_TCHAR), + 1); + + ACE_DEBUG ((LM_DEBUG, + "Connection to %s %s\n", + buffer, + connection_status[i] == Svc_Handler::SUCCEEDED ? + "succeeded" : "failed")); + + if (connection_status[i] == Svc_Handler::SUCCEEDED) + { + svc_handlers[i]->close (); + } + } + + delete[] svc_handlers; + delete[] connection_status; +} + +void +test (ACE_Reactor_Impl *impl) +{ + size_t nr_names = sizeof hosts / sizeof (char *); + ACE_INET_Addr *addresses = + new ACE_INET_Addr[nr_names]; + + for (size_t i = 0, number_of_connections = 0; i < nr_names; ++i) + { + if (addresses[number_of_connections].set (hosts[i]) == 0) + ++number_of_connections; + else + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("%p\n"), + ACE_TEXT_CHAR_TO_TCHAR (hosts[i]))); + } + + ACE_Reactor reactor (impl, + 1); + + int complete_nonblocking_connections = 1; + int dont_wait_for_nonblocking_connections = 0; + int ignored = 99; + + ACE_Synch_Options blocking_connect = + ACE_Synch_Options::defaults; + + ACE_DEBUG ((LM_DEBUG, + "\nBlocking connections...\n\n")); + + test_connect (reactor, + addresses, + blocking_connect, + ignored); + + blocking_connect.set (ACE_Synch_Options::USE_TIMEOUT, + ACE_Time_Value (0, 50 * 1000)); + + ACE_DEBUG ((LM_DEBUG, + "\nBlocking connections (with timeouts)...\n\n")); + + test_connect (reactor, + addresses, + blocking_connect, + ignored); + + ACE_Synch_Options nonblocking_connect + (ACE_Synch_Options::USE_REACTOR); + + ACE_DEBUG ((LM_DEBUG, + "\nNon-blocking connections...\n\n")); + + test_connect (reactor, + addresses, + nonblocking_connect, + complete_nonblocking_connections); + + ACE_DEBUG ((LM_DEBUG, + "\nNon-blocking connections (without waiting for completions)...\n\n")); + + test_connect (reactor, + addresses, + nonblocking_connect, + dont_wait_for_nonblocking_connections); + + nonblocking_connect.set (ACE_Synch_Options::USE_REACTOR | + ACE_Synch_Options::USE_TIMEOUT, + ACE_Time_Value (0, 500 * 1000)); + + ACE_DEBUG ((LM_DEBUG, + "\nNon-blocking connections (with timeouts)...\n\n")); + + test_connect (reactor, + addresses, + nonblocking_connect, + complete_nonblocking_connections); + + delete[] addresses; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:b:c:")); + + int cc; + while ((cc = get_opt ()) != -1) + { + switch (cc) + { + case 'a': + test_select_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + test_tp_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + test_wfmo_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case '?': + case 'u': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\nusage: %s \n\n") + ACE_TEXT ("\t[-a test Select Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-b test TP Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-c test WFMO Reactor] (defaults to %d)\n") + ACE_TEXT ("\n"), + argv[0], + test_select_reactor, + test_tp_reactor, + test_wfmo_reactor)); + return -1; + } + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("NonBlocking_Conn_Test")); + + // Validate options. + int result = + parse_args (argc, argv); + if (result != 0) + return result; + + if (test_select_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting Select Reactor....\n\n")); + + test (new ACE_Select_Reactor); + } + + if (test_tp_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting TP Reactor....\n\n")); + + test (new ACE_TP_Reactor); + } + +#if defined (ACE_WIN32) + + if (test_wfmo_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting WFMO Reactor....\n\n")); + + test (new ACE_WFMO_Reactor); + } + +#endif /* ACE_WIN32 */ + + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/NonBlocking_Conn_Test.h b/ACE/tests/NonBlocking_Conn_Test.h new file mode 100644 index 00000000000..77746238226 --- /dev/null +++ b/ACE/tests/NonBlocking_Conn_Test.h @@ -0,0 +1,54 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// NonBlocking_Conn_Test.cpp +// +// = DESCRIPTION +// This test checks for the proper working of the following: +// - blocking connections +// - blocking connections with timeouts +// - non-blocking connections +// - non-blocking connections without waiting for completions +// - non-blocking connections with timeouts +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#ifndef NONBLOCKING_CONN_TEST_H +#define NONBLOCKING_CONN_TEST_H + +#include "ace/Svc_Handler.h" +#include "ace/SOCK_Stream.h" + +class Svc_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + + enum Connection_Status + { + SUCCEEDED, + FAILED + }; + + Svc_Handler (void); + + void connection_status (Connection_Status &status, + int &completion_counter); + + int open (void *); + + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask mask); + + Connection_Status *status_; + int *completion_counter_; +}; + +#endif /* NONBLOCKING_CONN_TEST_H */ diff --git a/ACE/tests/Notification_Queue_Unit_Test.cpp b/ACE/tests/Notification_Queue_Unit_Test.cpp new file mode 100644 index 00000000000..98d19f61fef --- /dev/null +++ b/ACE/tests/Notification_Queue_Unit_Test.cpp @@ -0,0 +1,329 @@ +/** + * @file Notification_Queue_Unit_Test.cpp + * + * $Id$ + * + * A unit test for the ACE_Notification_Queue class. + * + * @author Carlos O'Ryan <coryan@atdesk.com> + * + */ + +#include "test_config.h" +#include "ace/Notification_Queue.h" + +ACE_RCSID(tests, + Notification_Queue_Unit_Test, "$Id$") + +#define TEST_LIST \ + ACTION(null_test) \ + ACTION(pop_returns_element_pushed) \ + ACTION(purge_empty_queue) \ + ACTION(purge_with_no_matches) \ + ACTION(purge_with_single_match) \ + ACTION(purge_with_multiple_matches) \ + ACTION(reset_empty_queue) \ + ACTION(reset_non_empty_queue) \ + +// Declare all the tests +#define ACTION(TEST_NAME) void TEST_NAME (char const * test_name); +TEST_LIST +#undef ACTION + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Notification_Queue_Unit_Test")); + + // Call all the tests +#define ACTION(TEST_NAME) TEST_NAME (#TEST_NAME); +TEST_LIST +#undef ACTION + + ACE_END_TEST; + + return 0; +} + +// There are far more elegant ways to do this. Ideally one would use +// an existing framework (Boost.Test, TUT, CppTest). But this will +// do for our purposes +void test_equal(int x, int y, char const * x_msg, char const * y_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno); +void test_equal(void * x, void * y, char const * x_msg, char const * y_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno); +void test_not_equal(int x, int y, char const * x_msg, char const * y_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno); +void test_assert(bool predicate, char const * predicate_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno); + +#define TEST_EQUAL(X, Y, MSG) \ + test_equal((X), (Y), #X, #Y, MSG, test_name, __FILE__, __LINE__) +#define TEST_NOT_EQUAL(X, Y, MSG) \ + test_not_equal((X), (Y), #X, #Y, MSG, test_name, __FILE__, __LINE__) +#define TEST_ASSERT(PREDICATE, MESSAGE) \ + test_assert((PREDICATE), #PREDICATE, MESSAGE, test_name, __FILE__, __LINE__) + +void null_test(char const * test_name) +{ + ACE_Notification_Queue queue; + + TEST_EQUAL(0, 0, "Test framework failure"); + TEST_NOT_EQUAL(1, 0, "Test framework failure"); + TEST_ASSERT(true, "True is still true"); +} + +class Event_Handler : public ACE_Event_Handler +{ +public: + Event_Handler(int event_handler_id) + : ACE_Event_Handler() + , id (event_handler_id) + { + } + + int id; +}; + +void pop_returns_element_pushed(char const * test_name) +{ + ACE_Notification_Queue queue; + + Event_Handler eh1(1); + Event_Handler eh2(2); + Event_Handler eh3(2); + + int result = queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::READ_MASK)); + TEST_ASSERT(result == 1, "push[1] should return 1"); + + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + TEST_ASSERT(result == 0, "push[2] should return 0"); + + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh3, + ACE_Event_Handler::READ_MASK | + ACE_Event_Handler::WRITE_MASK)); + TEST_ASSERT(result == 0, "push[3] should return 0"); + + ACE_Notification_Buffer current; + bool more_messages_queued; + ACE_Notification_Buffer next; + + result = queue.pop_next_notification(current, more_messages_queued, next); + TEST_ASSERT(result == 1, "pop[0] should return 1"); + TEST_ASSERT(more_messages_queued, "pop[0] should have more messages"); + + TEST_EQUAL(current.eh_, &eh1, "Wrong handler extracted"); + TEST_EQUAL(current.mask_, ACE_Event_Handler::READ_MASK, + "Wrong mask extracted"); + + result = queue.pop_next_notification(current, more_messages_queued, next); + TEST_ASSERT(result == 1, "pop[1] should return 1"); + TEST_ASSERT(more_messages_queued, "pop[1] should have more messages"); + + TEST_EQUAL(current.eh_, &eh2, "Wrong handler extracted"); + TEST_EQUAL(current.mask_, ACE_Event_Handler::WRITE_MASK, + "Wrong mask extracted"); + + result = queue.pop_next_notification(current, more_messages_queued, next); + TEST_ASSERT(result == 1, "pop[2] should return 1"); + TEST_ASSERT(!more_messages_queued, "pop[2] should not have more messages"); + + TEST_EQUAL(current.eh_, &eh3, "Wrong handler extracted"); + TEST_EQUAL(current.mask_, ACE_Event_Handler::READ_MASK | + ACE_Event_Handler::WRITE_MASK, + "Wrong mask extracted"); + + more_messages_queued = true; + result = queue.pop_next_notification(current, more_messages_queued, next); + TEST_ASSERT(result == 0, "pop[3] should return 0"); + TEST_ASSERT(!more_messages_queued, "pop[3] should not have more messages"); +} + +void purge_empty_queue(char const * test_name) +{ + ACE_Notification_Queue queue; + + Event_Handler eh1(1); + + int result = queue.purge_pending_notifications(&eh1, + ACE_Event_Handler::READ_MASK); + TEST_ASSERT(result == 0, "purge of empty queue should return 0"); +} + +void purge_with_no_matches(char const * test_name) +{ + ACE_Notification_Queue queue; + + Event_Handler eh1(1); + Event_Handler eh2(2); + + int result = queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::READ_MASK)); + + result = queue.purge_pending_notifications(&eh2, + ACE_Event_Handler::READ_MASK); + TEST_ASSERT(result == 0, "purge of eh2 should return 0"); + + result = queue.purge_pending_notifications(&eh1, + ACE_Event_Handler::WRITE_MASK); + TEST_ASSERT(result == 0, "purge of eh1/WRITE should return 0"); +} + +void purge_with_single_match(char const * test_name) +{ + ACE_Notification_Queue queue; + + Event_Handler eh1(1); + Event_Handler eh2(2); + + int result = queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::READ_MASK | + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::READ_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + + result = queue.purge_pending_notifications(&eh2, + ACE_Event_Handler::READ_MASK); + TEST_EQUAL(result, 1, "purge of eh2/READ should return 1"); + + result = queue.purge_pending_notifications(&eh1, + ACE_Event_Handler::READ_MASK); + TEST_EQUAL(result, 0, "purge of eh1/READ should return 0"); +} + +void purge_with_multiple_matches(char const * test_name) +{ + ACE_Notification_Queue queue; + + Event_Handler eh1(1); + Event_Handler eh2(2); + + int result = queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::READ_MASK | + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::READ_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + result = queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + + result = queue.purge_pending_notifications(&eh2, + ACE_Event_Handler::WRITE_MASK); + TEST_EQUAL(result, 3, "purge of eh2/WRITE should return 3"); + + result = queue.purge_pending_notifications(&eh1, + ACE_Event_Handler::WRITE_MASK); + TEST_EQUAL(result, 1, "purge of eh1/WRITE should return 1"); +} + +void reset_empty_queue(char const * /* test_name */) +{ + ACE_Notification_Queue queue; + + queue.reset(); +} + +void reset_non_empty_queue(char const * /* test_name */) +{ + ACE_Notification_Queue queue; + + Event_Handler eh1(1); + Event_Handler eh2(2); + + queue.push_new_notification( + ACE_Notification_Buffer(0, + ACE_Event_Handler::READ_MASK)); + queue.push_new_notification( + ACE_Notification_Buffer(&eh1, + ACE_Event_Handler::READ_MASK)); + queue.push_new_notification( + ACE_Notification_Buffer(&eh2, + ACE_Event_Handler::WRITE_MASK)); + queue.push_new_notification( + ACE_Notification_Buffer(0, + ACE_Event_Handler::WRITE_MASK)); + + queue.reset(); +} + +void test_equal(int x, int y, char const * x_msg, char const * y_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno) +{ + if (x == y) return; + ACE_ERROR ((LM_ERROR, + ACE_TEXT("%C in (%C %C:%d) %C (%d) != %C (%d)\n"), + error_message, + test_name, filename, lineno, + x_msg, x, y_msg, y)); +} + +void test_equal(void * x, void * y, char const * x_msg, char const * y_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno) +{ + if (x == y) return; + ACE_ERROR ((LM_ERROR, + ACE_TEXT("%C in (%C %C:%d) %C (%@) != %C (%@)\n"), + error_message, + test_name, filename, lineno, + x_msg, x, y_msg, y)); +} + +void test_not_equal(int x, int y, char const * x_msg, char const * y_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno) +{ + if (x != y) return; + ACE_ERROR ((LM_ERROR, + ACE_TEXT("%C in (%C %C:%d) %C (%d) != %C (%d)\n"), + error_message, + test_name, filename, lineno, + x_msg, x, y_msg, y)); +} + +void test_assert(bool predicate, char const * predicate_msg, + char const * error_message, + char const * test_name, char const * filename, int lineno) +{ + if (predicate) return; + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Assertion in (%C %C:%d) %C %C\n"), + test_name, filename, lineno, + predicate_msg, error_message)); +} diff --git a/ACE/tests/Notify_Performance_Test.cpp b/ACE/tests/Notify_Performance_Test.cpp new file mode 100644 index 00000000000..9f7d71d9207 --- /dev/null +++ b/ACE/tests/Notify_Performance_Test.cpp @@ -0,0 +1,259 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Notify_Performance_Test.cpp +// +// = DESCRIPTION +// This test is used to time the notification mechanisms of the +// ACE_Reactors. Both the WFMO_Reactor and Select_Reactor can be +// tested. The notify() mechanism can also be tested with or +// without data. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Profile_Timer.h" +#include "ace/Get_Opt.h" +#include "ace/Thread_Manager.h" +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/Dev_Poll_Reactor.h" +#include "ace/Auto_Ptr.h" +#include "ace/Atomic_Op.h" + +ACE_RCSID(tests, Notify_Performance_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +// Number of client (user) threads +static long opt_nthreads = 1; + +// Number of notify calls +static int opt_nloops = 20000; + +// Use the WFMO_Reactor +static int opt_wfmo_reactor = 0; + +// Use the Select_Reactor +static int opt_select_reactor = 0; + +// Use the Dev_Poll_Reactor +static int opt_dev_poll_reactor = 0; + +// Pass data through the notify call +static int opt_pass_notify_data = 0; + +// Simple no-op handler +class Handler : public ACE_Event_Handler +{ +public: + virtual int handle_exception (ACE_HANDLE fd = ACE_INVALID_HANDLE); + // The Handler callbacks. +}; + +int +Handler::handle_exception (ACE_HANDLE handle) +{ + ACE_UNUSED_ARG (handle); + + return 0; +} + +// Execute the client tests. + +static void * +client (void *arg) +{ + // Number of client (user) threads + static ACE_Atomic_Op<ACE_Thread_Mutex, long> thread_counter; + thread_counter = opt_nthreads; + + // To pass or not to pass is the question + Handler *handler = 0; + if (!opt_pass_notify_data) + handler = 0; + else + handler = (Handler *) arg; + + for (int i = 0; i < opt_nloops; i++) + ACE_Reactor::instance ()->notify (handler); + + if (--thread_counter == 0) + ACE_Reactor::instance()->end_reactor_event_loop (); + + return 0; +} + +// Sets up the correct reactor (based on platform and options) + +static void +create_reactor (void) +{ + ACE_Reactor_Impl *impl = 0; + + if (opt_wfmo_reactor) + { +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + ACE_NEW (impl, ACE_WFMO_Reactor); +#endif /* ACE_WIN32 */ + } + else if (opt_select_reactor) + { + ACE_NEW (impl, ACE_Select_Reactor); + } + else if (opt_dev_poll_reactor) + { +#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) + ACE_NEW (impl, ACE_Dev_Poll_Reactor); +#endif /* ACE_HAS_EVENT_POLL || ACE_HAS_DEV_POLL */ + } + ACE_Reactor *reactor = 0; + ACE_NEW (reactor, ACE_Reactor (impl)); + ACE_Reactor::instance (reactor); +} + +static void +print_results (ACE_Profile_Timer::ACE_Elapsed_Time &et) +{ + const ACE_TCHAR *reactor_type = 0; + if (opt_wfmo_reactor) + reactor_type = ACE_TEXT ("WFMO_Reactor"); + else if (opt_select_reactor) + reactor_type = ACE_TEXT ("Select_Reactor"); + else if (opt_dev_poll_reactor) + reactor_type = ACE_TEXT ("Dev_Poll_Reactor"); + else + reactor_type = ACE_TEXT ("Platform's default Reactor"); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nNotify_Performance Test statistics:\n\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tReactor Type: %s\n"), + reactor_type)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tWorker threads (calling notify()): %d\n"), + opt_nthreads)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tIteration per thread: %d\n"), + opt_nloops)); + if (opt_pass_notify_data) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tData was passed in the notify() call\n"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tNo data was passed in the notify() call\n"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\tTiming results notify() call:\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\t\treal time = %f secs \n\t\tuser time = %f secs \n\t\tsystem time = %f secs\n\n"), + et.real_time, + et.user_time, + et.system_time)); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Notify_Performance_Test")); + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("pswdc:l:")); + + for (int c; (c = getopt ()) != -1; ) + switch (c) + { + //FUZZ: enable check_for_lack_ACE_OS + case 'p': + opt_dev_poll_reactor = 1; + break; + case 's': + opt_select_reactor = 1; + break; + case 'w': + opt_wfmo_reactor = 1; + break; + case 'c': + opt_nthreads = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'l': + opt_nloops = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'd': + opt_pass_notify_data = 1; + break; + } + + // Sets up the correct reactor (based on platform and options) + create_reactor (); + + // Manage memory automagically. + auto_ptr<ACE_Reactor> reactor (ACE_Reactor::instance ()); + auto_ptr<ACE_Reactor_Impl> impl; + + // If we are using other that the default implementation, we must + // clean up. + if (opt_select_reactor || opt_wfmo_reactor || opt_dev_poll_reactor) + { + auto_ptr<ACE_Reactor_Impl> auto_impl (ACE_Reactor::instance ()->implementation ()); + impl = auto_impl; + } + + // Callback object + Handler handler; + + // Spawn worker threads + if (ACE_Thread_Manager::instance ()->spawn_n + (opt_nthreads, + ACE_THR_FUNC (client), + (void *) &handler, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n%a"), ACE_TEXT ("thread create failed"))); + + // Timer business + ACE_Profile_Timer timer; + timer.start (); + + // Run event loop + ACE_Reactor::instance()->run_reactor_event_loop (); + + timer.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + timer.elapsed_time (et); + + // Print results + print_results (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) waiting for the worker threads...\n"))); + + // Wait for all worker to get done. + ACE_Thread_Manager::instance ()->wait (); + + ACE_END_TEST; + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Notify_Performance_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/OS_Test.cpp b/ACE/tests/OS_Test.cpp new file mode 100644 index 00000000000..cbe0f4e96de --- /dev/null +++ b/ACE/tests/OS_Test.cpp @@ -0,0 +1,1027 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This simple test exercises and illustrates use of OS wrapper functions. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_math.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_strings.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_errno.h" + +ACE_RCSID(tests, OS_Test, "$Id$") + +#undef THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL +#define THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL(X) \ + ((X) \ + ? static_cast<void>(0) \ + : ACE_VERSIONED_NAMESPACE_NAME::__ace_assert(__FILE__, __LINE__, ACE_TEXT_CHAR_TO_TCHAR (#X))) + +// Test ACE_OS::access() to be sure a file's existence is correctly noted. +int +access_test (void) +{ + int test_status = 0; + + int status = ACE_OS::access (ACE_TEXT ("missing_file.txt"), F_OK); + if (status == -1) + { + if (errno == ENOTSUP) + ACE_ERROR_RETURN ((LM_INFO, + ACE_TEXT ("ACE_OS::access() not supported\n")), + 0); + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Missing file noted as present.\n"))); + test_status = -1; + } + + return test_status; +} + +// Test ACE_OS::rename to be sure the files come and go as expected. +int +rename_test (void) +{ +#if defined (ACE_LACKS_RENAME) || defined (ACE_VXWORKS) + // On VxWorks only some filesystem drivers support rename + // and as we do not know which is used, skip the test here + ACE_ERROR_RETURN ((LM_INFO, + ACE_TEXT ("rename not supported on this platform\n")), + 0); +#else + ACE_TCHAR old_file[MAXPATHLEN]; + ACE_TCHAR new_file[MAXPATHLEN]; + ACE_OS::strcpy (old_file, ACE_TEXT ("rename_test_old")); + ACE_OS::strcpy (new_file, ACE_TEXT ("rename_test_new")); + + // Test 1. Rename old to new when new already exists. + // To set up, create two files, old and new. Both get opened and truncated + // in case they're left over from a previous run. The first one (old) gets + // something written in it so it's non-zero length - this is how the rename + // is verified. + FILE *f = ACE_OS::fopen (old_file, ACE_TEXT ("w+")); + if (f == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s: %p\n"), + old_file, + ACE_TEXT ("fopen")), + -1); + // Write something in the old_file so it has non-zero length + ACE_OS::fputs (ACE_TEXT ("this is a test\n"), f); + ACE_OS::fclose (f); + f = ACE_OS::fopen (new_file, ACE_TEXT ("w+")); + if (f == 0) + { + ACE_OS::unlink (old_file); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s: %p\n"), + new_file, + ACE_TEXT ("fopen")), + -1); + } + ACE_OS::fclose (f); + +#if defined (ACE_WIN32) && defined (ACE_LACKS_WIN32_MOVEFILEEX) + // Can't rename if new_file exists already. + ACE_OS::unlink (new_file); +#endif + + if (ACE_OS::rename (old_file, new_file) != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("rename test 1"))); + ACE_OS::unlink (old_file); + ACE_OS::unlink (new_file); + return -1; + } + // Verify that the old file was really renamed. + ACE_stat checking; + int result = 0; + if (ACE_OS::stat (new_file, &checking) == -1 || checking.st_size == 0) + { + result = -1; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Rename test 1: new_file not correct\n"))); + } + if (ACE_OS::stat (old_file, &checking) == 0) + { + result = -1; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Rename test 1: old_file still there\n"))); + } + if (result == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Rename when dest. exists: success\n"))); + + // Now test 2 - rename when the new file does not exist. If test 1 worked, + // the old_file is now new_file and there is no old_file. + if (ACE_OS::rename (new_file, old_file) != 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("rename test 2"))); + ACE_OS::unlink (old_file); + ACE_OS::unlink (new_file); + return -1; + } + if (ACE_OS::stat (old_file, &checking) == -1 || checking.st_size == 0) + { + result = -1; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Rename test 2: new_file not correct\n"))); + } + else if (ACE_OS::stat (new_file, &checking) == 0) + { + result = -1; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Rename test 2: old_file still there\n"))); + } + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Rename when dest. gone: success\n"))); + + ACE_OS::unlink (new_file); + ACE_OS::unlink (old_file); + + // Test 3: It should fail... there are no files. + if (ACE_OS::rename (old_file, new_file) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Rename test 3 should bomb, and did.\n"))); + else + { + result = -1; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Rename expected fail, but succeeded\n"))); + } + + return result; +#endif /* ACE_LACKS_RENAME */ +} + +// +int +string_emulation_test (void) +{ + { + // ======================================================================== + // Test memchr + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing memchr\n"))); + + const char *memchr1 = "abcdefghijklmnopqrstuvwxyz"; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::memchr (static_cast<const void *> (0), + 'a', + 0) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::memchr (memchr1, 'a', sizeof (memchr1)) != 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::memchr (memchr1, '1', sizeof (memchr1)) == 0); + + // ======================================================================== + // Test strchr + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strchr\n"))); + + const char *strchr1 = "abcdefghijkabcdefghijk"; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (*ACE_OS::strchr (strchr1, 'h') == 'h'); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strchr (strchr1, 'h') == strchr1 + 7); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strchr (strchr1, '1') == 0); + + // ======================================================================== + // Test strrchr + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strrchr\n"))); + + const char *strrchr1 = "abcdefghijkabcdefghijk"; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (*ACE_OS::strrchr (strrchr1, 'h') == 'h'); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strrchr (strrchr1, 'h') == strrchr1 + 18); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strrchr (strrchr1, '1') == 0); + + // ======================================================================== + // Test strcspn + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strcspn\n"))); + + const char *strcspn1 = "abcdefghijkabcdefghijk"; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcspn (strcspn1, "d") == 3); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcspn (strcspn1, "abcdefghijk") == 0); + + // ======================================================================== + // Test strcasecmp + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strcasecmp\n"))); + + const char *strcasecmp1 = "stringf"; + const char *strcasecmp2 = "stringfe"; // An extra character + const char *strcasecmp3 = "stringg"; // The last letter is higher + const char *strcasecmp4 = "STRINGF"; // Different case + const char *strcasecmp5 = "stringe"; // The last letter is lower + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp2) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp3) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp4) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp5) > 0); + + // ======================================================================== + // Test strtok_r + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strtok_r\n"))); + + char strtok_r1[] = "A string of tokens"; + char *strtok_r2; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok_r (strtok_r1, + " ", + &strtok_r2), + "A") == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok_r (0, + " ", + &strtok_r2), + "string") == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok_r (0, + " ", + &strtok_r2), + "of") == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok_r (0, + " ", + &strtok_r2), + "tokens") == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strtok_r (0, " ", &strtok_r2) == 0); + + // ======================================================================== + // Test itoa + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing itoa\n"))); + + char itoa1[33]; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (42, itoa1, 2), + "101010") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (42, itoa1, 3), + "1120") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (42, itoa1, 16), + "2a") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (8, itoa1, 10), + "8") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (-8, itoa1, 10), + "-8") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (20345, itoa1, 10), + "20345") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (-20345, itoa1, 10), + "-20345") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (4566733, itoa1, 10), + "4566733") == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (-4566733, itoa1, 10), + "-4566733") == 0); + } + +#if defined (ACE_HAS_WCHAR) + { + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test itoa (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing itoa (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + wchar_t itow1[33]; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (42, itow1, 2), + ACE_TEXT_WIDE ("101010")) == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (42, itow1, 3), + ACE_TEXT_WIDE ("1120")) == 0); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::itoa (42, itow1, 16), + ACE_TEXT_WIDE ("2a")) == 0); + + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strcmp (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strcmp (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strcmp1 = ACE_TEXT_WIDE ("stringf"); + const wchar_t *strcmp2 = ACE_TEXT_WIDE ("stringfe"); + const wchar_t *strcmp3 = ACE_TEXT_WIDE ("stringg"); + const wchar_t *strcmp4 = ACE_TEXT_WIDE ("STRINGF"); + const wchar_t *strcmp5 = ACE_TEXT_WIDE ("stringe"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcmp1, strcmp1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcmp1, strcmp2) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcmp1, strcmp3) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcmp1, strcmp4) != 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcmp1, strcmp5) > 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strcpy (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strcpy (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strcpy1 = ACE_TEXT_WIDE ("abcdefghijklmnopqrstuvwxyz"); + wchar_t strcpy2[27]; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strcpy (strcpy2, strcpy1), + strcpy1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcpy2, strcpy1) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strcat (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strcat (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strcat1 = ACE_TEXT_WIDE ("abcdefghijklmnopqrstuvwxyz"); + wchar_t strcat2[27] = ACE_TEXT_WIDE ("abcdefghijkl"); + const wchar_t *strcat3 = ACE_TEXT_WIDE ("mnopqrstuvwxyz"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strcat (strcat2, strcat3), + strcat1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strcat2, strcat1) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strncat (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strncat (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strncat1 = ACE_TEXT_WIDE ("abcdefghijklmnopqrstuvwxyz"); + wchar_t strncat2[27] = ACE_TEXT_WIDE ("abcdefghijkl"); + const wchar_t *strncat3 = ACE_TEXT_WIDE ("mnopqrstuvwxyzabc"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strncat (strncat2, strncat3, 14), + strncat1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strncat2, strncat1) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strspn (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strspn (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strspn1 = ACE_TEXT_WIDE ("abcdefghijkabcdefghijk"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strspn (strspn1, + ACE_TEXT_WIDE ("abcdf")) == 4); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strspn (strspn1, + ACE_TEXT_WIDE ("mno")) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strchr (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strchr (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strchr1 = ACE_TEXT_WIDE ("abcdefghijkabcdefghijk"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (*ACE_OS::strchr (strchr1, ACE_TEXT_WIDE ('h')) + == ACE_TEXT_WIDE ('h')); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strchr (strchr1, ACE_TEXT_WIDE ('h')) + == strchr1 + 7); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strchr (strchr1, ACE_TEXT_WIDE ('1')) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strstr (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strstr (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strstr1 = ACE_TEXT_WIDE ("abcdefghijkabcdefghijk"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strncmp ( + ACE_OS::strstr (strstr1, ACE_TEXT_WIDE ("def")), + ACE_TEXT_WIDE ("def"), + 3) + == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strstr (strstr1, + ACE_TEXT_WIDE ("mno")) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strlen (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strlen (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strlen1 = ACE_TEXT_WIDE (""); + const wchar_t *strlen2 = ACE_TEXT_WIDE ("12345"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strlen (strlen1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strlen (strlen2) == 5); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strpbrk (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strpbrk (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strpbrk1 = ACE_TEXT_WIDE ("abcdefghijkabcdefghijk"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strpbrk (strpbrk1, ACE_TEXT_WIDE ("ijkb")) + == strpbrk1 + 1); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strpbrk (strpbrk1, + ACE_TEXT_WIDE ("mno")) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strrchr (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strrchr (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strrchr1 = ACE_TEXT_WIDE ("abcdefghijkabcdefghijk"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (*ACE_OS::strrchr (strrchr1, ACE_TEXT_WIDE ('h')) + == ACE_TEXT_WIDE ('h')); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strrchr (strrchr1, ACE_TEXT_WIDE ('h')) + == strrchr1 + 18); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strrchr (strrchr1, ACE_TEXT_WIDE ('1')) + == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strcasecmp (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strcasecmp (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strcasecmp1 = ACE_TEXT_WIDE ("stringf"); + const wchar_t *strcasecmp2 = ACE_TEXT_WIDE ("stringfe"); + const wchar_t *strcasecmp3 = ACE_TEXT_WIDE ("stringg"); + const wchar_t *strcasecmp4 = ACE_TEXT_WIDE ("STRINGF"); + const wchar_t *strcasecmp5 = ACE_TEXT_WIDE ("stringe"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp1) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp2) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp3) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp4) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcasecmp (strcasecmp1, strcasecmp5) > 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strncasecmp (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strncasecmp (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strncasecmp1 = ACE_TEXT_WIDE ("stringf"); + const wchar_t *strncasecmp2 = ACE_TEXT_WIDE ("stringfe"); + const wchar_t *strncasecmp3 = ACE_TEXT_WIDE ("stringg"); + const wchar_t *strncasecmp4 = ACE_TEXT_WIDE ("STRINGF"); + const wchar_t *strncasecmp5 = ACE_TEXT_WIDE ("stringe"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncasecmp (strncasecmp1, strncasecmp2, 7) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncasecmp (strncasecmp1, strncasecmp2, 8) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncasecmp (strncasecmp1, strncasecmp3, 7) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncasecmp (strncasecmp1, strncasecmp4, 7) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncasecmp (strncasecmp1, strncasecmp5, 7) > 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strncmp (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strncmp (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + const wchar_t *strncmp1 = ACE_TEXT_WIDE ("stringf"); + const wchar_t *strncmp2 = ACE_TEXT_WIDE ("stringfe"); + const wchar_t *strncmp3 = ACE_TEXT_WIDE ("stringg"); + const wchar_t *strncmp4 = ACE_TEXT_WIDE ("STRINGF"); + const wchar_t *strncmp5 = ACE_TEXT_WIDE ("stringe"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strncmp (strncmp1, strncmp2, 7) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strncmp (strncmp1, strncmp2, 8) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strncmp (strncmp1, strncmp3, 7) < 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strncmp (strncmp1, strncmp4, 7) != 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strncmp (strncmp1, strncmp5, 7) > 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strncpy (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strncpy (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + wchar_t strncpy1[] = ACE_TEXT_WIDE ("abcdefghijklmnopqrstuvwxyzabc"); + wchar_t strncpy2[27]; + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strncpy (strncpy2, + strncpy1, + 26), + strncpy1, + 26) == 0); + + strncpy1[26] = 0; + strncpy2[26] = 0; + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strncpy2, strncpy1) == 0); + + //FUZZ: disable check_for_lack_ACE_OS + // ======================================================================== + // Test strtok (wchar_t version) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing strtok (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + wchar_t strtok_r1[] = ACE_TEXT_WIDE ("A string of tokens"); + + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok (strtok_r1, + ACE_TEXT_WIDE (" ")), + ACE_TEXT_WIDE ("A")) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok (0, + ACE_TEXT_WIDE (" ")), + ACE_TEXT_WIDE ("string") ) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok (0, + ACE_TEXT_WIDE (" ")), + ACE_TEXT_WIDE ("of") ) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (ACE_OS::strtok (0, + ACE_TEXT_WIDE (" ")), + ACE_TEXT_WIDE ("tokens") ) == 0); + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strtok (0, ACE_TEXT_WIDE (" ")) == 0); + + + } +#endif /* ACE_HAS_WCHAR */ + + return 0; +} + +// Test ACE_OS::snprintf +int +snprintf_test (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing snprintf\n"))); + + int error_count = 0; + const int BUFFER_SIZE = 4; + char buf[2*BUFFER_SIZE]; + int retval; + + ACE_OS::memset(buf, 0xab, 2*BUFFER_SIZE); + retval = ACE_OS::snprintf (buf, BUFFER_SIZE, "%d", 123); + if (retval != 3) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[1] ACE_OS::snprintf() returns %d, should be 3\n"), + retval)); + ++error_count; + } + + ACE_OS::memset(buf, 0xab, 2*BUFFER_SIZE); + retval = ACE_OS::snprintf (buf, BUFFER_SIZE, "%d", 1234); + + // HP-UX has broken vsnprintf +#if !defined (HPUX) + if (retval != 4) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[2] ACE_OS::snprintf() returns %d, should be 4\n"), + retval)); + ++error_count; + } +#endif /* !HPUX */ + + if (buf[3] != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[3] ACE_OS::snprintf() doesn't terminate string correctly\n"))); + ++error_count; + } + else if (ACE_OS::strcmp(buf, "123") != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[4] ACE_OS::snprintf() incorrect output\n"))); + ++error_count; + } + + ACE_OS::memset(buf, 0xab, 2*BUFFER_SIZE); + retval = ACE_OS::snprintf (buf, BUFFER_SIZE, "%d", 12345); + if (retval != 5) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[5] ACE_OS::snprintf() returns %d, should be 5\n"), + retval)); + ++error_count; + } + else if (buf[3] != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[6] ACE_OS::snprintf() doesn't terminate string correctly\n"))); + ++error_count; + } + else if (ACE_OS::strcmp(buf, "123") != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("[6] ACE_OS::snprintf() incorrect output\n"))); + ++error_count; + } + + return error_count; +} + +static int +ctime_r_test (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing ctime_r\n"))); + + int result = 0; + + // test 'normal' buffer + ACE_TCHAR buf[27]; + buf[26] = 'Z'; + + ACE_Time_Value cur_time = + ACE_OS::gettimeofday (); + + time_t secs = cur_time.sec (); + if (ACE_OS::ctime_r (&secs, buf, 26) == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("ctime_r with 26 bytes"))); + result = -1; + } + else if (buf[0] == '\0') + { + result = -1; + + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Truncated input buffer\n"))); + } + else if (buf[26] != 'Z') + { + result = -1; + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Wrote past end of input buffer\n"))); + } + + // test small buffer - should not do anything unless 3rd arg is at least 26. + if (result == 0) + { + ACE_TCHAR bufcheck[27]; + ACE_OS::strcpy (bufcheck, buf); + if (ACE_OS::ctime_r (&secs, buf, 10) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ctime_r with short len returned %s\n"), + buf)); + result = -1; + } + else if (errno != ERANGE) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ctime_r short; wrong error"))); + result = -1; + } + // Make sure it didn't scribble + else if (ACE_OS::strcmp (buf, bufcheck) != 0) + { + result = -1; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ctime_r short; got %s, expected %s\n"), + buf, bufcheck)); + } + } + + return result; +} + + +int +string_strsncpy_test (void) +{ + { + //FUZZ: disable check_for_lack_ACE_OS + // Test strsncpy (char version) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing strsncpy (char version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + char strsncpy1[] = "abcdefghijklmnopqrstuvwxyzabc"; + char strsncpy2[36]; + + // strsncpy() where the max. length doesn't matter + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 36), + strsncpy1) == 0); + + // strsncpy() where the max length does matter + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 26), + strsncpy1, + 25) == 0); + + // strsncpy1 and strsncpy2 are different size --> not equal + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strsncpy2, strsncpy1) != 0); + + // max. length == 2 --> 1 char available + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 2), + strsncpy1, + 1) == 0); + + // max length == 1 --> empty string + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strlen (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 1)) == 0); + + // just preparation for the next assert + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 36), + strsncpy1) == 0); + + // A tricky one, if the max. length == 0 --> do nothing + // so the strsncpy2 shouldn't change + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strsncpy (strsncpy2, + "test", + 0), + strsncpy1) == 0); + + // If src == dst --> truncate dst if needed! + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strsncpy (strsncpy2, + strsncpy2, + 10), + strsncpy1, + 9) == 0); + // size should be 9 (+ '\0' char) + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL(ACE_OS::strlen(strsncpy2) == 9); + + } + +#if defined (ACE_HAS_WCHAR) + { + //FUZZ: disable check_for_lack_ACE_OS + // Test strsncpy (wchar_t version) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing strsncpy (wchar_t version)\n"))); + //FUZZ: enable check_for_lack_ACE_OS + + wchar_t strsncpy1[] = ACE_TEXT_WIDE ("abcdefghijklmnopqrstuvwxyzabc"); + wchar_t strsncpy2[36]; + + // strsncpy() where the max. length doesn't matter + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 36), + strsncpy1) == 0); + + // strsncpy() where the max length does matter + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 26), + strsncpy1, + 25) == 0); + + // strsncpy1 and strsncpy2 are different size --> not equal + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL (ACE_OS::strcmp (strsncpy2, strsncpy1) != 0); + + // max. length == 2 --> 1 char available + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 2), + strsncpy1, + 1) == 0); + + // max length == 1 --> empty string + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strlen (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 1)) == 0); + + // just preparation for the next assert + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strsncpy (strsncpy2, + strsncpy1, + 36), + strsncpy1) == 0); + + // A tricky one, if the max. length == 0 --> do nothing + // so the strsncpy2 shouldn't change + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strcmp (ACE_OS::strsncpy (strsncpy2, + ACE_TEXT_WIDE + ("test"), + 0), + strsncpy1) == 0); + + // If src == dst --> truncate dst if needed! + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL + (ACE_OS::strncmp (ACE_OS::strsncpy (strsncpy2, + strsncpy2, + 10), + strsncpy1, + 9) == 0); + // size should be 9 (+ '\0' char) + THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL(ACE_OS::strlen(strsncpy2) == 9); + } +#endif /* ACE_HAS_WCHAR */ + + return 0; +} + + +// Test conversion between narrow and wide chars. +int +string_convert_test (void) +{ +#if defined (ACE_HAS_WCHAR) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing narrow/wide string conversion\n"))); + + int result = 0; + const char *test1_n = "abcdefg"; + const wchar_t *test1_w = ACE_TEXT_WIDE ("abcdefg"); + const char *test2_n = "\xe9\xe8\xe0\xf9\xea"; + const wchar_t *test2_w = ACE_TEXT_WIDE ("\xe9\xe8\xe0\xf9\xea"); + wchar_t str_w[10]; + char str_n[10]; + ACE_OS::strcpy (str_w, ACE_Ascii_To_Wide (test1_n).wchar_rep ()); + if (0 != ACE_OS::strcmp (test1_w, str_w)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Simple narrow->wide failed: ") + ACE_TEXT ("Expected \"%W\"; Got \"%W\"\n"), test1_w, str_w)); + result = 1; + } + ACE_OS::strcpy (str_n, ACE_Wide_To_Ascii (test1_w).char_rep ()); + if (0 != ACE_OS::strcmp (test1_n, str_n)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Simple wide->narrow failed: ") + ACE_TEXT ("Expected \"%C\"; Got \"%C\"\n"), test1_n, str_n)); + result = 1; + } + ACE_OS::strcpy (str_w, ACE_Ascii_To_Wide (test2_n).wchar_rep ()); + if (0 != ACE_OS::strcmp (test2_w, str_w)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Complex narrow->wide failed: ") + ACE_TEXT ("Expected \"%W\"; Got \"%W\"\n"), test2_w, str_w)); + result = 1; + } + ACE_OS::strcpy (str_n, ACE_Wide_To_Ascii (test2_w).char_rep ()); + if (0 != ACE_OS::strcmp (test2_n, str_n)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Complex wide->narrow failed: ") + ACE_TEXT ("Expected \"%C\"; Got \"%C\"\n"), test2_n, str_n)); + result = 1; + } + return result; +#else + return 0; +#endif /* ACE_HAS_WCHAR */ +} + +// Test the methods for getting cpu info +int +cpu_info_test (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing cpu info methods\n"))); + + long number_processors = ACE_OS::num_processors(); + long number_processors_online = ACE_OS::num_processors_online(); + + if (number_processors == -1) + { + ACE_ERROR ((LM_INFO, + ACE_TEXT ("number of processors not supported on ") + ACE_TEXT ("this platform\n"))); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This system has %d processors\n"), + number_processors)); + } + + if (number_processors_online == -1) + { + ACE_ERROR ((LM_INFO, + ACE_TEXT ("number of processors online not supported on ") + ACE_TEXT ("this platform\n"))); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("This system has %d processors online\n"), + number_processors_online)); + } + + return 0; +} + +int +pagesize_test (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing getpagesize method\n"))); + + long const pagesize = ACE_OS::getpagesize (); + if (pagesize <= 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Error, pagesize should return a value bigger ") + ACE_TEXT ("then zero, it returned %d\n"), pagesize)); + return 1; + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Pagesize returned %d\n"), + pagesize)); + } + return 0; +} + +int +log2_test (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing log2 method\n"))); + + double values[] = {1.0, 2.0, 4.0, 8.0, 1048576.0}; + int results[] = {0, 1, 2, 3, 20}; + int result = 0; + int error_count = 0; + + for (size_t i = 0 ; i < sizeof (values) / sizeof (double) ; i++) + { + result = static_cast<int> (ACE_OS::log2 (values [i]) + 0.5); + if (result != results [i]) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Log2 error: input %.1F, output %d, expected %d\n"), values [i], result, results [i])); + error_count++; + } + } + + return error_count; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("OS_Test")); + + int status = 0; + int result; + + if ((result = access_test ()) != 0) + status = result; + + if ((result = rename_test ()) != 0) + status = result; + + if ((result = string_emulation_test ()) != 0) + status = result; + +#if !defined (ACE_LACKS_VSNPRINTF) || defined (ACE_HAS_TRIO) + if ((result = snprintf_test ()) != 0) + status = result; +#endif /* !ACE_LACKS_VSNPRINTF || ACE_HAS_TRIO */ + + if ((result = ctime_r_test ()) != 0) + status = result; + + if ((result = string_strsncpy_test ()) != 0) + status = result; + + if ((result = cpu_info_test ()) != 0) + status = result; + + if ((result = pagesize_test ()) != 0) + status = result; + + if ((result = log2_test ()) != 0) + status = result; + + ACE_END_TEST; + return status; +} +#undef THIS_IS_NOT_AN_ASSERT_IT_IS_A_NON_DEBUG_TEST_AS_WELL diff --git a/ACE/tests/Object_Manager_Flipping_Test.cpp b/ACE/tests/Object_Manager_Flipping_Test.cpp new file mode 100644 index 00000000000..e2dc4349d39 --- /dev/null +++ b/ACE/tests/Object_Manager_Flipping_Test.cpp @@ -0,0 +1,128 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Object_Manager_Flipping_Test.cpp +// +// = DESCRIPTION + +// Tests the basic function of the ACE_Service_Config in scenarios +// where the ACE_Object_Manager is being +// flipped. i.e. ACE::init/ACE::fini() are called in sequence +// (which can occur with loading DLLs). More specifically we test +// that the OM correctly controlls the lifecycle of the default SC +// instance, which is pointed to by a TSS pointer. +// +// = AUTHOR +// Iliyan Jeliazkov <iliyan@ociweb.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Object_Manager.h" +#include "ace/OS_Memory.h" +#include "ace/ACE.h" +#include "ace/Service_Config.h" + +ACE_RCSID(tests, Object_Manager_Flipping_Test, "$Id$") + +static u_int *ip; + +static const bool is_static_object_manager = +#if defined (ACE_HAS_STATIC_OBJECT_MANAGER) + true; +#else + false; +#endif + +extern "C" +void +hook1 (void) +{ + delete ip; + ip = 0; +} + + +int run_main (int, ACE_TCHAR *[]) +{ + + // Causing the creation of a SC instance and the corresponding TSS + // key. It is not registered with the Object Manager, but beware - + // OM finalization will destroy it too. + ACE_Service_Gestalt *p0 = ACE_Service_Config::instance (); + + ACE_Service_Gestalt *p1 = 0; + u_int errors = 0; + + // ... + { + ACE::init (); + ACE_START_TEST (ACE_TEXT ("Object_Manager_Flipping_Test")); + + + // If hook1 never gets called, this will show up as a memory leak. + ACE_NEW_RETURN (ip, + u_int, + -1); + + if (ACE_OS::atexit (hook1) != 0) + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::atexit () returned non-zero!!!!"))); + } + + // Obtain a SC instance which will be later used to compare with others. + p1 = ACE_Service_Config::instance (); + + // ACE_ASSERT uses Log_Msg::instance() and needs to be done only + // after ACE_START_TEST + + ACE_ASSERT (p0 == p1); + ACE_ASSERT (!p0->is_opened ()); + ACE_END_TEST; + + + // ACE::fini() destroys the SC (unmanaged) singleton ... + // Next time we ask for one, it will be a different instance. + ACE::fini (); + } + + p1 = ACE_Service_Config::instance (); + + // This is a legitimate test, but more importantly an + // attemp to dereference p1 should succeed. If SC's TSS + // was not cleaned correctly this will SEGV. As will the + // following ACE::init, as it tries to use the SC instance. + + if (p1->is_opened ()) + ++errors; + + // Not using ACE_ASSERT because ACE is not initialized yet. + + { + ACE::init(); + ACE_START_TEST (ACE_TEXT ("Object_Manager_Flipping_Test")); + + ACE_Service_Gestalt *p2 = ACE_Service_Config::instance (); + + // ACE_ASSERT uses Log_Msg::instance() and needs to be done only + // after ACE_START_TEST + ACE_ASSERT (p1 == p2); + + // An attempt to dereference should be fine. + ACE_ASSERT (!p2->is_opened ()); + + ACE_END_TEST; + ACE::fini(); // Flipped twice + } + + return errors == 0 ? 0 : 1; + +} diff --git a/ACE/tests/Object_Manager_Test.cpp b/ACE/tests/Object_Manager_Test.cpp new file mode 100644 index 00000000000..79cd94c8ccd --- /dev/null +++ b/ACE/tests/Object_Manager_Test.cpp @@ -0,0 +1,119 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Object_Manager_Test.cpp +// +// = DESCRIPTION +// Tests the basic functions of the ACE_Object_Manager. +// +// = AUTHOR +// David L. Levine <levine@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Object_Manager.h" +#include "ace/OS_Memory.h" +#include "ace/ACE.h" + +ACE_RCSID(tests, Object_Manager_Test, "$Id$") + +static u_int *ip; + +extern "C" +void +hook1 (void) +{ + delete ip; + ip = 0; +} + +extern "C" +void +hook2 (void * /* object */, void *param) +{ + u_int *paramp = reinterpret_cast<u_int *> (param); + + // We can use ACE_Log_Msg in an ACE_Object_Manager cleanup hook. + // But NOT in an ACE_OS::atexit () hook! However, the ACE_END_TEST + // invocation in main () will prevent this from being output to the + // log stream. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("hook2: %d\n"), + *paramp)); + delete paramp; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE::init (); + + ACE_START_TEST (ACE_TEXT ("Object_Manager_Test")); + + u_int errors = 0; + + // If hook1 never gets called, this will show up as a memory leak. + ACE_NEW_RETURN (ip, + u_int, + -1); + + const int starting_up = + ACE_Object_Manager::instance ()->starting_up (); + const int shutting_down = + ACE_Object_Manager::instance ()->shutting_down (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("starting_up: %d, shutting_down: %d\n"), + starting_up, + shutting_down)); + + if (starting_up || shutting_down) + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("starting_up and shutting_down are supposed to ") + ACE_TEXT (" be 0!!!!"))); + } + + if (ACE_OS::atexit (hook1) != 0) + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::atexit () returned non-zero!!!!"))); + } + + for (u_int i = 0; i < 10; ++i) + { + u_int *paramp; + ACE_NEW_RETURN (paramp, + u_int, + -1); + *paramp = i; + + // The first paramp argument is only used to distinguish the + // at_exit entries. The ACE_Object_Manager only allows one + // at_exit per object. It's not used in the hook. + if (ACE_Object_Manager::instance ()->at_exit (paramp, + hook2, + paramp)) + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_Object_Manager::at_exit () ") + ACE_TEXT ("returned non-zero!!!!"))); + } + } + + ACE_END_TEST; + ACE::fini (); + return errors == 0 ? 0 : 1; +} diff --git a/ACE/tests/Obstack_Test.cpp b/ACE/tests/Obstack_Test.cpp new file mode 100644 index 00000000000..e6539f32b3f --- /dev/null +++ b/ACE/tests/Obstack_Test.cpp @@ -0,0 +1,116 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Obtack_Test.cpp +// +// = DESCRIPTION +// Checks the functionality of ACE_Obstack<T> +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Obstack.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(tests, Obstack_Test, "$Id$") + + +int run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Obstack_Test")); + + int errors = 0; + // For this test, the length of the ACE_Obstack must be larger than + // both of these strings, but less than their sum. + const ACE_TCHAR str1[] = ACE_TEXT ("Mary had a little lamb."); + const ACE_TCHAR str2[] = ACE_TEXT ("It's fleece was white as snow; but...."); + ACE_Obstack_T<ACE_TCHAR> stack (sizeof (str1) + 1); + + for (size_t i = 0; i < ACE_OS::strlen (str1); i++) + stack.grow_fast (str1[i]); + + ACE_TCHAR *str = stack.freeze (); + + if (str == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("freeze failed!\n"))); + ++errors; + } + else if (ACE_OS::strcmp (str, str1) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Lost first string; expected: %s, have: %s\n"), + str1, str)); + ++errors; + } + + for (size_t j = 0; j < ACE_OS::strlen (str2); ++j) + stack.grow (str2[j]); + + ACE_TCHAR* temp = stack.freeze(); + + if (temp == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("freeze failed!\n"))); + ++errors; + } + else if (ACE_OS::strcmp (temp, str2) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("Lost second string; expected: %s, have: %s\n"), + str2, temp)); + ++errors; + } + + for (size_t k = 0; k < ACE_OS::strlen (str1); ++k) + stack.grow (str1[k]); + + ACE_TCHAR* tmp = stack.freeze (); + if (tmp == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("freeze failed!\n"))); + ++errors; + } + else if (ACE_OS::strcmp (tmp, str1) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Lost third string; expected: %s, have: %s\n"), + str1, tmp)); + ++errors; + } + + stack.unwind (temp); + + for (size_t l = 0; l < ACE_OS::strlen (str2); ++l) + stack.grow (str2[l]); + + temp = stack.freeze(); + + if (temp == 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("freeze failed!\n"))); + ++errors; + } + else if (ACE_OS::strcmp (temp, str2) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Lost fourth string; expected: %s, have: %s\n"), + str2, temp)); + ++errors; + } + + if (!errors) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test ok\n"))); + + ACE_END_TEST; + return errors == 0 ? 0 : 1; +} diff --git a/ACE/tests/OrdMultiSet_Test.cpp b/ACE/tests/OrdMultiSet_Test.cpp new file mode 100644 index 00000000000..689cbd7d13c --- /dev/null +++ b/ACE/tests/OrdMultiSet_Test.cpp @@ -0,0 +1,220 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// OrdMultiSet_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the <ACE_Ordered_MultiSet> and +// <ACE_Ordered_MultiSet_Iterator> class templates, instantiating +// them with type int. No command line arguments are needed to run +// the test. +// +// = AUTHOR +// Chris Gill <cdgill@cs.wustl.edu> +// +// ============================================================================ + +// Note, for this test the config.h file *must* come first! +#include "ace/config-all.h" + +#include "test_config.h" +#include "ace/Containers.h" + +ACE_RCSID(tests, OrdMultiSet_Test, "$Id$") + +int +run_main (int, ACE_TCHAR *[]) +{ + int ret = 0; + int *ptr = 0; + + ACE_START_TEST (ACE_TEXT ("OrdMultiSet_Test")); + + // make an empty set of int and an iterator + ACE_Ordered_MultiSet<int> set; + ACE_Ordered_MultiSet_Iterator<int> iter(set); + + // Put in a range of odd ints, without an iterator. + int i; + for (i = -10; i < 10; ++i) + set.insert (2 * i + 1); + + // Put in an interleaved range of even ints, using an iterator. + for (i = -10; i <= 10; ++i) + set.insert (2 * i, iter); + + // Remove the first and last elements of range. + while (set.remove (-20) == 0); + while (set.remove (20) == 0); + + // Should still have 39 elements in the set. + ACE_ASSERT (set.is_empty () == 0); + ACE_ASSERT (set.size () == 39); + + // Iterate forward through the range we created: should be one of + // each. + iter.first (); + for (i = -19; i <= 19; ++i) + { + // we should still be in the set + ACE_ASSERT (iter.done () == 0); + + // make sure the current element is what we expect + iter.next (ptr); + ACE_ASSERT (ptr != 0); + ACE_ASSERT (*ptr == i); + + // move to the next element in the set + iter.advance (); + } + + // We should have iterated through the entire set. + ACE_ASSERT (iter.done () != 0); + + // Iterate backward through the range we created: should be one of + // each. + iter.last (); + for (i = 19; i >= -19; --i) + { + // We should still be in the set. + ACE_ASSERT (iter.done () == 0); + + // Make sure the current element is what we expect. + int *ptr = 0; + iter.next (ptr); + ACE_ASSERT (ptr != 0); + ACE_ASSERT (*ptr == i); + + // Move to the previous element in the set. + iter.retreat (); + } + + // We should have iterated through the entire set. + ACE_ASSERT (iter.done () != 0); + + // Iterate through the set and use the operator* to get the element + iter.first (); + for (i = -19; i <= 19; ++i) + { + // we should still be in the set + ACE_ASSERT (iter.done () == 0); + + // make sure the current element is what we expect + int& l = *iter; + ACE_ASSERT (l == i); + + // move to the next element in the set + iter.advance (); + } + + // We should have iterated through the entire set. + ACE_ASSERT (iter.done () != 0); + + // Clear the set, restart the iterator, and make sure the iterator + // is out of range at both ends, the set is empty, and a subsequent + // advance or retreat on an out of range iterator does not cause + // problems + set.reset (); + ACE_ASSERT (set.is_empty () != 0); + iter.first (); + ACE_ASSERT (iter.done () != 0); + iter.retreat (); + iter.last (); + ACE_ASSERT (iter.done () != 0); + iter.advance (); + + // Put in a bunch of ints in various relative positions, using an + // iterator for the odds and no iterator for the evens. + set.insert (203, iter); + set.insert (202); + set.insert (204); + set.insert (201, iter); + set.insert (205, iter); + + set.insert (203, iter); + set.insert (203, iter); + + set.insert (204); + set.insert (204); + set.insert (204); + set.insert (205, iter); + set.insert (205, iter); + set.insert (205, iter); + set.insert (205, iter); + set.insert (202); + + // remove the middle elements + while (set.remove (204) == 0); + while (set.remove (202) == 0); + while (set.remove (203) == 0); + + // Put the iterator out of range and make sure it stays + // that way for finds on the missing elements. + iter.last (); + iter.advance (); + set.find (203, iter); + ACE_ASSERT (iter.done () != 0); + set.find (202, iter); + ACE_ASSERT (iter.done () != 0); + set.find (204, iter); + ACE_ASSERT (iter.done () != 0); + + // Make sure the other elements can be found. + set.find (205, iter); + ACE_ASSERT (iter.done () == 0); + iter.next (ptr); + ACE_ASSERT (ptr != 0); + ACE_ASSERT (*ptr == 205); + set.find (201, iter); + ACE_ASSERT (iter.done () == 0); + iter.next (ptr); + ACE_ASSERT (ptr != 0); + ACE_ASSERT (*ptr == 201); + + // Finally, iterate through the set and make sure its contents are + // correct (one 201 and five 205s). + iter.first (); + ACE_ASSERT (iter.done () == 0); + iter.next (ptr); + ACE_ASSERT (ptr != 0); + ACE_ASSERT (*ptr == 201); + iter.advance (); + + for (i = 1; i <= 5; ++i) + { + // Should be in the set, able to access the element, value + // should be 205 + ACE_ASSERT (iter.done () == 0); + iter.next (ptr); + ACE_ASSERT (ptr != 0); + ACE_ASSERT (*ptr == 205); + + // Move to the next element in the set. + iter.advance (); + } + + // Should not be anything else in the set. + ACE_ASSERT (iter.done () != 0); + + // remove the rest + while (set.remove (205) == 0); + while (set.remove (201) == 0); + + // Should have no more elements in the set. + ACE_ASSERT (set.is_empty () != 0); + ACE_ASSERT (set.size () == 0); + iter.first (); + ACE_ASSERT (iter.done () != 0); + iter.last (); + ACE_ASSERT (iter.done () != 0); + + ACE_END_TEST; + + return ret; +} + diff --git a/ACE/tests/Pipe_Test.cpp b/ACE/tests/Pipe_Test.cpp new file mode 100644 index 00000000000..ad7bb728646 --- /dev/null +++ b/ACE/tests/Pipe_Test.cpp @@ -0,0 +1,177 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Pipe_Test.cpp +// +// = DESCRIPTION +// Tests the construction of multiple pipes in a process. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Pipe.h" +#include "ace/Process.h" +#include "ace/Get_Opt.h" +#include "ace/ACE.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Pipe_Test, "$Id$") + +// Indicates whether we should close the pipe or not. +static int close_pipe = 1; + +// Indicates whether we're running as the child or the parent. +static int child_process = 0; + +// Number of iterations to run the test. +static int iterations = ACE_MAX_ITERATIONS; + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-d (don't close pipes)] ") + ACE_TEXT ("[-c (child process)] [-i (iterations)] \n"))); + ACE_OS::exit (1); +} + +// Parse the command-line arguments and set options. + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("dci:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'd': + close_pipe = 0; + break; + case 'c': + child_process = 1; + break; + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + print_usage_and_die (); + break; + } +} + +// Consolidate the ACE_Pipe initializations. + +//FUZZ: disable check_for_lack_ACE_OS +static void +open (ACE_Pipe &pipe, + const char *name) +{ +//FUZZ: enable check_for_lack_ACE_OS + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("opening %C\n"), name)); + int result = pipe.open (); + + ACE_ASSERT (result != -1); + result = pipe.read_handle () != ACE_INVALID_HANDLE + && pipe.write_handle () != ACE_INVALID_HANDLE; + ACE_ASSERT (result == 1); + + if (close_pipe) + pipe.close (); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + parse_args (argc, argv); + + if (child_process) + { + ACE_APPEND_LOG (ACE_TEXT("Pipe_Test-children")); + ACE_Pipe a, b, c, d, e; + + //FUZZ: disable check_for_lack_ACE_OS + open (a, "a"); + open (b, "b"); + open (c, "c"); + open (d, "d"); + open (e, "e"); + //FUZZ: enable check_for_lack_ACE_OS + + ACE_END_LOG; + } + else + { + ACE_START_TEST (ACE_TEXT("Pipe_Test")); + ACE_INIT_LOG (ACE_TEXT("Pipe_Test-children")); + +# if defined (ACE_WIN32) || !defined (ACE_USES_WCHAR) + const ACE_TCHAR *cmdline_fmt = ACE_TEXT ("%s -c%s"); +# else + const ACE_TCHAR *cmdline_fmt = ACE_TEXT ("%ls -c%ls"); +# endif /* ACE_WIN32 || !ACE_USES_WCHAR */ + ACE_Process_Options options; + options.command_line (cmdline_fmt, + argv[0], + close_pipe == 0 ? ACE_TEXT (" -d") : ACE_TEXT ("")); + + ACE_exitcode status = 0; + + for (int i = 0; i < ::iterations; i++) + { + ACE_Process server; + + if (server.spawn (options) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn failed")), + -1); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Server forked with pid = %d.\n"), + server.getpid ())); + } + + // Wait for the process we just created to exit. + server.wait (&status); + + // Check if child exited without error. + if (WIFEXITED (status) != 0 + && WEXITSTATUS (status) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Child of server %d finished with error ") + ACE_TEXT ("exit status %d\n"), + server.getpid (), + WEXITSTATUS (status))); + + ACE_END_TEST; + + ACE_OS::exit (WEXITSTATUS (status)); + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Server %d finished\n"), + server.getpid ())); + } + ACE_END_TEST; + } + + return 0; +} diff --git a/ACE/tests/Priority_Buffer_Test.cpp b/ACE/tests/Priority_Buffer_Test.cpp new file mode 100644 index 00000000000..074ffce90e7 --- /dev/null +++ b/ACE/tests/Priority_Buffer_Test.cpp @@ -0,0 +1,182 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Priority_Buffer_Test.cpp +// +// = DESCRIPTION +// This is a simple test to illustrate the priority mechanism of +// <ACE_Message_Queue>s. The producer uses an <ACE_Message_Queue> +// to enqueue a bunch of messages with different priorities which +// are then dequeued by the consumer. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Message_Queue.h" +#include "ace/Thread_Manager.h" + +ACE_RCSID(tests, Priority_Buffer_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// Global message count. +static int message_count = 0; + +// Make the queue be capable of being *very* large. +static const long max_queue = LONG_MAX; + +// The consumer dequeues a message from the ACE_Message_Queue, writes +// the message to the stderr stream, and deletes the message. The +// producer sends a 0-sized message to inform the consumer to stop +// reading and exit. + +static void * +consumer (void *args) +{ + ACE_Message_Queue<ACE_MT_SYNCH> *msg_queue = + reinterpret_cast<ACE_Message_Queue<ACE_MT_SYNCH> *> (args); + + u_long cur_priority = 27; + ACE_UNUSED_ARG (cur_priority); + // To suppress ghs warning about unused local variable + // "cur_priority". + + int local_count = 0; + + // Keep looping, reading a message out of the queue, until we get a + // message with a length == 0, which signals us to quit. + for (char c = 'z'; ; c--) + { + ACE_Message_Block *mb = 0; + + int result = msg_queue->dequeue_head (mb); + + if (result == -1) + break; + + local_count++; + + size_t length = mb->length (); + + if (length > 0) + { + // This isn't a "shutdown" message, so process it + // "normally." + ACE_ASSERT (c == *mb->rd_ptr ()); + ACE_ASSERT (mb->msg_priority () < cur_priority); + cur_priority = mb->msg_priority (); + } + + // Free up the buffer memory and the Message_Block. Note that + // the destructor of Message Block will delete the the actual + // buffer. + mb->release (); + + if (length == 0) + // This was a "shutdown" message, so break out of the loop. + break; + } + + ACE_ASSERT (local_count == message_count); + return 0; +} + +// The producer reads data from the stdin stream, creates a message, +// and then queues the message in the message list, where it is +// removed by the consumer thread. A 0-sized message is enqueued when +// there is no more data to read. The consumer uses this as a flag to +// know when to exit. + +static void * +producer (void *args) +{ + ACE_Message_Queue<ACE_MT_SYNCH> *msg_queue = + reinterpret_cast<ACE_Message_Queue<ACE_MT_SYNCH> *> (args); + + ACE_Message_Block *mb = 0; + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + { + ++message_count; + + // Allocate a new message + + ACE_NEW_RETURN (mb, + ACE_Message_Block (1), + 0); + *mb->wr_ptr () = *c; + + // Set the priority. + mb->msg_priority (message_count); + mb->wr_ptr (1); + + // Enqueue in priority order. + if (msg_queue->enqueue_prio (mb) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("put_next")), + 0); + } + + // Now send a 0-sized shutdown message to the other thread + ACE_NEW_RETURN (mb, + ACE_Message_Block ((size_t) 0), + 0); + + if (msg_queue->enqueue_tail (mb) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("put_next"))); + + ++message_count; + + // Now read all the items out in priority order (i.e., ordered by + // the size of the lines!). + consumer (msg_queue); + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Spawn off one thread that copies stdin to stdout in order of the +// size of each line. + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Priority_Buffer_Test")); + +#if defined (ACE_HAS_THREADS) + // Message queue. + ACE_Message_Queue<ACE_MT_SYNCH> msg_queue (max_queue); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (producer), + (void *) &msg_queue, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn")), + 1); + + // Wait for producer and consumer threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Priority_Reactor_Test.cpp b/ACE/tests/Priority_Reactor_Test.cpp new file mode 100644 index 00000000000..463b43a22e7 --- /dev/null +++ b/ACE/tests/Priority_Reactor_Test.cpp @@ -0,0 +1,399 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Priority_Reactor_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_Priority_Reactor>. The test forks +// two processes (for a total of three processes) which connect to +// the main process and The clients send data to a connector, +// interestingly enough the acceptor will give more priority to +// the second connection, which should run always before the first +// one. +// +// The test itself is interesting, it shows how to write very +// simple <ACE_Svc_Handler>, <ACE_Connectors> and <ACE_Acceptors>. +// +// = AUTHOR +// Carlos O'Ryan <coryan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Get_Opt.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Acceptor.h" +#include "ace/Handle_Set.h" +#include "ace/Connector.h" +#include "ace/Auto_Ptr.h" +#include "ace/Priority_Reactor.h" +#include "Priority_Reactor_Test.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Priority_Reactor_Test, "$Id$") + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// The number of children to run, it can be changed using the -c +// option. +static int opt_nchildren = 10; + +// The number of loops per children, it can be changed using the -l +// option. +static int opt_nloops = 200; + +// If not set use the normal reactor, it can be changed using the -d +// option. +static int opt_priority_reactor = 1; + +// Maximum time to wait for the test termination (-t) +static int opt_max_duration = 60; + +// Maximum number of retries to connect, it can be changed using the +// -m option. +static int max_retries = 5; + +typedef ACE_Connector<Write_Handler, ACE_SOCK_CONNECTOR> + CONNECTOR; +typedef ACE_Acceptor<Read_Handler, ACE_SOCK_ACCEPTOR> + ACCEPTOR; + +int Read_Handler::waiting_ = 0; +int Read_Handler::started_ = 0; + +void +Read_Handler::set_countdown (int nchildren) +{ + Read_Handler::waiting_ = nchildren; +} + +int +Read_Handler::get_countdown (void) +{ + return Read_Handler::waiting_; +} + +int +Read_Handler::open (void *) +{ + if (this->peer ().enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Read_Handler::open, ") + ACE_TEXT ("cannot set non blocking mode")), + -1); + + if (reactor ()->register_handler (this, READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Read_Handler::open, ") + ACE_TEXT ("cannot register handler")), + -1); + + // A number larger than the actual number of priorities, so some + // clients are misbehaved, hence pusnished. + const int max_priority = 15; + + this->priority (ACE_Event_Handler::LO_PRIORITY + started_ % max_priority); + started_++; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) created svc_handler for handle %d ") + ACE_TEXT ("with priority %d\n"), + get_handle (), + priority ())); + return 0; +} + +int +Read_Handler::handle_input (ACE_HANDLE h) +{ + // ACE_DEBUG((LM_DEBUG, + // "(%P|%t) Read_Handler::handle_input (%d)\n", h)); + ACE_UNUSED_ARG (h); + + char buf[BUFSIZ]; + + ssize_t result = this->peer ().recv (buf, sizeof (buf)); + + if (result <= 0) + { + if (result < 0 && errno == EWOULDBLOCK) + return 0; + + if (result != 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Read_Handler::handle_input"))); + waiting_--; + + if (waiting_ == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Last svc_handler closed, shutting down\n"))); + ACE_Reactor::instance()->end_reactor_event_loop(); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Read_Handler::handle_input closing down\n"))); + return -1; + } + + // ACE_DEBUG((LM_DEBUG, + // "(%P|%t) read %d bytes from handle %d, priority %d\n", + // result, h, priority ())); + return 0; +} + +int +Write_Handler::open (void *) +{ + return 0; +} + +int +Write_Handler::svc (void) +{ + // Send several short messages, doing pauses between each message. + // The number of messages can be controlled from the command line. + ACE_Time_Value pause (0, 1000); + + for (int i = 0; i < opt_nloops; ++i) + { + if (this->peer ().send_n (ACE_ALPHABET, + sizeof (ACE_ALPHABET) - 1) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("send_n"))); + ACE_OS::sleep (pause); + } + + return 0; +} + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +// Execute the client tests. +static void * +client (void *arg) +{ + ACE_INET_Addr *connection_addr = + reinterpret_cast<ACE_INET_Addr *> (arg); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) running client\n"))); + CONNECTOR connector; + + Write_Handler *writer = 0; + + // Do exponential backoff connections + ACE_Synch_Options options = ACE_Synch_Options::synch; + + // Start with one msec timeouts. + ACE_Time_Value msec (0, 1000); + options.timeout (msec); + + // Try up to <max_retries> to connect to the server. + for (int i = 0; i < max_retries; i++) + { + if (connector.connect (writer, + *connection_addr, + options) == -1) + { + // Double the timeout... + ACE_Time_Value tmp = options.timeout (); + tmp += options.timeout (); + options.timeout (tmp); + writer = 0; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) still trying to connect\n"))); + } + else + { + // Let the new Svc_Handler to its job... + writer->svc (); + + // then close the connection and release the Svc_Handler. + writer->destroy (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) finishing client\n"))); + return 0; + } + } + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) failed to connect after %d retries\n"), + max_retries)); + return 0; +} + +#endif + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Priority_Reactor_Test")); + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("dc:l:m:t:")); + + for (int c; (c = getopt ()) != -1; ) + switch (c) + { + //FUZZ: enable check_for_lack_ACE_OS + case 'd': + opt_priority_reactor = 0; + break; + case 'c': + opt_nchildren = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'l': + opt_nloops = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'm': + max_retries = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 't': + opt_max_duration = ACE_OS::atoi (getopt.opt_arg ()); + break; + case '?': + default: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Usage: Priority_Reactor_Test ") + ACE_TEXT (" [-d] (disable priority reactor)\n") + ACE_TEXT (" [-c nchildren] (number of threads/processes)\n") + ACE_TEXT (" [-l loops] (number of loops per child)\n") + ACE_TEXT (" [-m maxretries] (attempts to connect)\n") + ACE_TEXT (" [-t max_time] (limits test duration)\n")), + -1); + ACE_NOTREACHED (break); + } + + // Manage Reactor memory automagically. + // Note: If opt_priority_reactor is false, the default ACE_Reactor is used + // and we don't need to set one up. + ACE_Reactor *orig_reactor = 0; + auto_ptr<ACE_Reactor> reactor; + + if (opt_priority_reactor) + { + ACE_Select_Reactor *impl_ptr; + ACE_NEW_RETURN (impl_ptr, ACE_Priority_Reactor, -1); + auto_ptr<ACE_Select_Reactor> auto_impl (impl_ptr); + + ACE_Reactor *reactor_ptr; + ACE_NEW_RETURN (reactor_ptr, ACE_Reactor (impl_ptr, 1), -1); + auto_impl.release (); // ACE_Reactor dtor will take it from here + auto_ptr<ACE_Reactor> auto_reactor (reactor_ptr); + reactor = auto_reactor; + orig_reactor = ACE_Reactor::instance (reactor_ptr); + } + + Read_Handler::set_countdown (opt_nchildren); + + // Acceptor + ACCEPTOR acceptor; + + acceptor.priority (ACE_Event_Handler::HI_PRIORITY); + ACE_INET_Addr server_addr; + + // Bind acceptor to any port and then find out what the port was. + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1 + || acceptor.acceptor ().get_local_addr (server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("open")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + + ACE_INET_Addr connection_addr (server_addr.get_port_number (), + ACE_DEFAULT_SERVER_HOST); + + int i; + +#if defined (ACE_HAS_THREADS) + for (i = 0; i < opt_nchildren; ++i) + { + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + (void *) &connection_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + } +#elif !defined (ACE_LACKS_FORK) + for (i = 0; i < opt_nchildren; ++i) + { + switch (ACE_OS::fork ("child")) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + ACE_OS::exit (-1); + /* NOTREACHED */ + case 0: + client (&connection_addr); + ACE_OS::exit (0); + break; + /* NOTREACHED */ + default: + break; + /* NOTREACHED */ + } + } +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_Time_Value tv (opt_max_duration); + + ACE_Reactor::instance()->register_handler + (&acceptor, ACE_Event_Handler::READ_MASK); + ACE_Reactor::instance()->run_reactor_event_loop (tv); + + if (Read_Handler::get_countdown () != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) running out of time, ") + ACE_TEXT ("probably due to failed connections.\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) waiting for the children...\n"))); + +#if defined (ACE_HAS_THREADS) + ACE_Thread_Manager::instance ()->wait (); +#elif !defined (ACE_WIN32) && !defined (VXWORKS) + for (i = 0; i < opt_nchildren; ++i) + { + pid_t pid = ACE_OS::wait(); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) child %d terminated\n"), + pid)); + } +#else + /* NOTREACHED */ + // We aborted on the previous #ifdef +#endif /* ACE_HAS_THREADS */ + + if (orig_reactor != 0) + ACE_Reactor::instance (orig_reactor); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Priority_Reactor_Test.h b/ACE/tests/Priority_Reactor_Test.h new file mode 100644 index 00000000000..fdf17865448 --- /dev/null +++ b/ACE/tests/Priority_Reactor_Test.h @@ -0,0 +1,76 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Priority_Reactor_Test.h +// +// = DESCRIPTION +// This class gets its own header file to work around AIX C++ +// compiler "features" related to template instantiation... It is +// only used by Priority_Reactor_Test.cpp. +// +// = AUTHOR +// Carlos O'Ryan <coryan@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_PRIORITY_REACTOR_TEST_H +#define ACE_TESTS_PRIORITY_REACTOR_TEST_H + +#include "ace/Service_Config.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/SOCK_Stream.h" +#include "ace/Svc_Handler.h" + +class Read_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_SYNCH> + // = TITLE + // A Svc_Handler with a priority twist. + // + // = DESCRIPTION + // This Svc_Handler receives the data sent by the childs or writer + // threads; each one sets it own priority to a new level, in a + // cyclic manner. The main point is test and exercise the + // priority dispatching features of ACE_Priority_Reactor. +{ +public: + static void set_countdown (int nchildren); + // Set the number of children or writer threads we will be running, + // when they are all gone we terminate the reactor loop. + + static int get_countdown (void); + // Get the number of children we are still waiting for. + + virtual int open (void *); + virtual int handle_input (ACE_HANDLE h); + // The Svc_Handler callbacks. + +private: + static int waiting_; + // How many writers are we waiting for. + + static int started_; + // How many readers have started. +}; + +class Write_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_SYNCH> + // = TITLE + // A simple writer. + // + // = DESCRIPTION + // This Svc_Handler simply connects to a server and sends some + // output to it. Its purpose is to feed the test. +{ +public: + virtual int open (void *); + virtual int svc (void); +}; + +#endif /* ACE_TESTS_PRIORITY_REACTOR_TEST_H */ diff --git a/ACE/tests/Priority_Task_Test.cpp b/ACE/tests/Priority_Task_Test.cpp new file mode 100644 index 00000000000..3ae0144dcd7 --- /dev/null +++ b/ACE/tests/Priority_Task_Test.cpp @@ -0,0 +1,253 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Priority_Task_Test.cpp +// +// = DESCRIPTION +// This is a simple test to illustrate the priority mechanism of +// ACE Tasks. The test requires no options, but the -d option +// enables LM_DEBUG output. +// +// = AUTHOR +// Carlos O'Ryan <coryan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Task.h" +#include "ace/Sched_Params.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_string.h" + +ACE_RCSID(tests, Priority_Task_Test, "$Id$") + +static const ACE_TCHAR *usage = ACE_TEXT ("usage: %s [-d]\n"); + +#if defined (ACE_HAS_THREADS) + +class Priority_Task : public ACE_Task<ACE_MT_SYNCH> +{ + // = TITLE + // A simple Task that runs itself a different priorities. + // + // = DESCRIPTION + // This task uses the void* argument on open to run the svc() + // method at a different priority. The point is execise the thread + // priority features of ACE. +public: + Priority_Task (void); + // The constructor + + //FUZZ: disable check_for_lack_ACE_OS + int open (void *); + // Receives the priority and run svc() on a separate thread at that + // priority. + //FUZZ: enable check_for_lack_ACE_OS + + int svc (void); + // Runs on a separate thread an checks the priority. + + int succeeded (void) { return error_ == 0; } + // Returns 1 if priority was set properly, 0 otherwise. + +private: + int priority_; + u_int error_; +}; + +Priority_Task::Priority_Task (void) + : ACE_Task<ACE_MT_SYNCH> (ACE_Thread_Manager::instance ()), + priority_ (0), + error_ (0) +{ +} + +int +Priority_Task::open (void *arg) +{ + this->priority_ = *(int *) arg; + + long flags = THR_NEW_LWP; + + // To get FIFO scheduling with PTHREADS. + ACE_SET_BITS (flags, + THR_SCHED_FIFO); + + // Become an active object. + if (this->activate (flags, + 1, + 0, + this->priority_) == -1) + { + // On Linux, for example, only the superuser can set the policy + // to other than ACE_SCHED_OTHER. But with ACE_SCHED_OTHER, + // there is only one thread priority value, for example, 0. So, + // let the superuser run an interesting test, but for other + // users use the minimum ACE_SCHED_OTHER thread priority. + + long fallback_priority = + ACE_Sched_Params::priority_min (ACE_SCHED_OTHER, + ACE_SCOPE_THREAD); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) task activation at priority %d with ") + ACE_TEXT ("flags 0x%X failed; retry at priority %d with ") + ACE_TEXT ("flags 0x%X (errno is %d%p)\n"), + this->priority_, + flags, + fallback_priority, + THR_NEW_LWP, + errno, + ACE_TEXT (""))); + + flags = THR_NEW_LWP; + this->priority_ = fallback_priority; + + if (this->activate (flags, + 1, + 1, + this->priority_) == -1) + { +#if !defined (ACE_HAS_WINCE) + if (ACE_OS::last_error () == EPERM) + ACE_ERROR_RETURN ((LM_INFO, + ACE_TEXT ("Insufficient privilege to run this test.\n")), + -1); + else +#endif // ACE_HAS_WINCE + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%t) task activation at priority %d failed, ") + ACE_TEXT ("exiting!\n%a"), + this->priority_, + -1)); + } + } + return 0; +} + +int +Priority_Task::svc (void) +{ + ACE_hthread_t thr_handle; + ACE_Thread::self (thr_handle); + int prio; + + if (ACE_Thread::getprio (thr_handle, prio) == -1) + { + if (errno == ENOTSUP) + { + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("getprior not supported on this platform\n") + )); + return 0; + } + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("getprio failed")), + -1); + } + + if (prio == this->priority_) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) actual prio of %d equals desired priority\n"), + prio)); + else + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%t) actual prio = %d, desired priority_ = %d!\n"), + prio, + this->priority_)); + ++error_; + } + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Priority_Task_Test")); + + if (argc <= 1) + // Disable LM_DEBUG messages. + ACE_Log_Msg::instance ()->priority_mask + (ACE_Log_Msg::instance ()->priority_mask () &~ LM_DEBUG); + else if (argc == 2) + { + if (ACE_OS::strcmp (argv[1], + ACE_TEXT ("-d")) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + usage, + argv [0]), + -1); + // else -d option: don't disable LM_DEBUG messages + } + else + ACE_ERROR_RETURN ((LM_ERROR, + usage, + argv [0]), + -1); + + int status = 0; + +#if defined (ACE_HAS_THREADS) + + Priority_Task tasks[ACE_MAX_ITERATIONS]; + + size_t i; + + // Spawn off ACE_MAX_ITERATIONS of tasks, passing each one their + // iteration number as their priority. + + // NOTE: on Solaris, for example, this requests the min FIFO + // priority. But, this test doesn't use the Realtime scheduling + // class. The FIFO priorities are used because they're all + // nonnegative. + + ACE_Sched_Priority_Iterator priority (ACE_SCHED_FIFO, + ACE_SCOPE_THREAD); + + for (i = 0; i < ACE_MAX_ITERATIONS; i++) + { + int p = priority.priority (); + if (tasks[i].open ((void *) &p) == -1) + break; // Out of enclosing loop. + + // If there are more priorities get the next one... + if (priority.more ()) + priority.next (); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %d tasks spawned, wait for them to exit . . .\n"), + ACE_MAX_ITERATIONS)); + + // Wait for all tasks to exit. + ACE_Thread_Manager::instance ()->wait (); + + for (i = 0; i < ACE_MAX_ITERATIONS; i++) + if (!tasks[i].succeeded ()) + { + ++status; + break; + } + +#else + ACE_ERROR ((LM_DEBUG, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + // Re-enable LM_DEBUG messages. + ACE_Log_Msg::instance ()->priority_mask + (ACE_Log_Msg::instance ()->priority_mask () | LM_DEBUG); + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/Proactor_Scatter_Gather_Test.cpp b/ACE/tests/Proactor_Scatter_Gather_Test.cpp new file mode 100644 index 00000000000..8c4d5543bfa --- /dev/null +++ b/ACE/tests/Proactor_Scatter_Gather_Test.cpp @@ -0,0 +1,1492 @@ +// $Id$ + +// ============================================================================ +/** + * @file Proactor_Scatter_Gather_Test.cpp + * + * The test runs on a single thread, and involves a single Sender, + * two Receivers and a single Writer. The Sender async-reads + * (scattered) from a file into chunks of <page size>. It + * async-sends (gathered) the odd chunks to the first receiver over a + * stream, and the even chunks to the second receiver over a + * different stream. The receivers async-read (scattered) from the + * socket streams into chunks in size of <page size>, and convey the + * data to the Writer. The Writer reconstructs the file using + * async-write (gathered). Then, the reconstructed file is compared + * to the original file to determine test success. So, It covers both + * async scatter/gather stream I/O and async scatter/gather file I/O. + * The wire transfer protocol is very naive (and totally non + * reliable...) - when both connections are closed, EOF is assumed. + * The test can be also run in a seperated sender and receiver mode, + * to test real network influences. + * + * This test is based upon some building blocks taken from the + * Proactor_Test.cpp. + * + * @author Edan Ayal <edanayal@yahoo.com> */ +// ============================================================================ + +#include "test_config.h" + +#if defined (ACE_HAS_WIN32_OVERLAPPED_IO) + // This currently only works on Win32 platforms (NT SP2 and above). + // Support for Unix platforms supporting POSIX aio calls should be added in future. + +#include "ace/Get_Opt.h" + +#include "ace/Proactor.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/Asynch_Connector.h" +#include "ace/Mem_Map.h" +#include "ace/Min_Max.h" +#include "ace/OS_NS_math.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/OS_NS_unistd.h" + +#include "ace/SOCK_Connector.h" + +// For the Acceptor/Connector handlers maintenance lists +static const int SENDERS = 1; +static const int RECEIVERS = 2; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +static const ACE_TCHAR *host = ACE_LOCALHOST; + +// File that we're sending. +static const ACE_TCHAR *input_file = ACE_TEXT("Proactor_Scatter_Gather_Test.cpp"); + +// Name of the output file. +static const ACE_TCHAR *output_file = ACE_TEXT("output"); + +static int client_only = 0; +static int server_only = 0; +static size_t chunk_size = 0; + +enum +{ + ODD = 0, + EVEN +}; + +// ************************************************************* +// Some chunks chain helper routines +// ************************************************************* +static int allocate_chunks_chain (ACE_Message_Block *&head_mb, + size_t number_of_chunks) +{ + ACE_Message_Block *pre_mb = 0; + + for (size_t index = 0; index < number_of_chunks; ++index) + { +#if defined (ACE_WIN32) + void *addr = ::VirtualAlloc (0, + chunk_size, + MEM_COMMIT, + PAGE_READWRITE); +#else + void *addr = new char[chunk_size]; +#endif /* ACE_WIN32 */ + if (addr) + { + ACE_Message_Block *mb = new ACE_Message_Block (static_cast<char *> (addr), + chunk_size); + if (!head_mb) + head_mb = mb; + + // chain them together + if (pre_mb) + pre_mb->cont (mb); + pre_mb = mb; + } + else + { + ACE_ASSERT (0); + return -1; + } + } + + return 0; +} + +static void +free_chunks_chain (ACE_Message_Block *&mb) +{ + for (const ACE_Message_Block* msg = mb; + msg != 0; + msg = msg->cont ()) + { +#if defined (ACE_WIN32) + ::VirtualFree (msg->base (), + msg->size (), + MEM_DECOMMIT); +#else + delete [] msg->base (); +#endif /* ACE_WIN32 */ + } + + mb->release (); + mb = 0; +} + +static int +last_chunk (ACE_Message_Block *chain, + ACE_Message_Block *&last) +{ + if (!chain) + return 0; + + int index = 1; + last = chain; + while (0 != last->cont ()) + { + last = last->cont (); + ++index; + } + + return index; +} + +static void +merge_odd_even_chains (ACE_Message_Block *odd_mb, + ACE_Message_Block *even_mb) +{ + ACE_Message_Block *pre_pre_mb = odd_mb; + ACE_Message_Block *pre_mb = even_mb; + ACE_Message_Block *curr_mb = odd_mb->cont (); + + if (even_mb) + { + for (; curr_mb != 0; curr_mb = pre_pre_mb->cont ()) + { + pre_pre_mb->cont (pre_mb); + + // increment history pointers + pre_pre_mb = pre_mb; + pre_mb = curr_mb; + } + + pre_pre_mb->cont (pre_mb); + pre_mb->cont (0); + } +} + +static void +split_odd_even_chains (ACE_Message_Block *odd_mb, + ACE_Message_Block *even_mb) +{ + ACE_Message_Block *pre_pre_mb = odd_mb; + ACE_Message_Block *pre_mb = even_mb; + ACE_Message_Block *curr_mb = (even_mb ? even_mb->cont () : 0); + + for (; curr_mb != 0; curr_mb = curr_mb->cont ()) + { + pre_pre_mb->cont (curr_mb); + + // increment history pointers + pre_pre_mb = pre_mb; + pre_mb = curr_mb; + } + + pre_pre_mb->cont (0); + if (pre_mb) + pre_mb->cont (0); +} + +static void +add_to_chunks_chain (ACE_Message_Block *&chunks_chain, + ACE_Message_Block *additional_chunks_chain) +{ + if (0 == chunks_chain) + chunks_chain = additional_chunks_chain; + else + { + ACE_Message_Block *last = 0; + last_chunk (chunks_chain, last); + if (last) + last->cont (additional_chunks_chain); + } +} + +static void +remove_empty_chunks (ACE_Message_Block *&chunks_chain) +{ + if (0 == chunks_chain) + return; + + ACE_Message_Block *first_empty = chunks_chain; + ACE_Message_Block *pre_mb = 0; + + while (first_empty->length () > 0 && + 0 != first_empty->cont ()) + { + pre_mb = first_empty; + first_empty = first_empty->cont (); + } + + // break the chain there, and release the empty end (might be everything) + if (0 == first_empty->length ()) + { + if (pre_mb) // might be 0, in case it's the entire chain + pre_mb->cont (0); + + if (first_empty == chunks_chain) + chunks_chain = 0; + + free_chunks_chain (first_empty); + } +} + +// ************************************************************* +// Acceptor, Receiver and Writer +// ************************************************************* +class Receiver; + +class Acceptor : public ACE_Asynch_Acceptor<Receiver> +{ + friend class Receiver; + +public: + Acceptor (void); + virtual ~Acceptor (void); + + void stop (void); + + // Virtual from ACE_Asynch_Acceptor + virtual Receiver *make_handler (void); + + int get_number_sessions (void) { return this->sessions_; } + +private: + void on_new_receiver (Receiver &rcvr); + void on_delete_receiver (Receiver &rcvr); + + int sessions_; + Receiver *list_receivers_[RECEIVERS]; +}; + +class Writer; + +// The first instantiated take the role of the odd receiver +class Receiver : public ACE_Service_Handler +{ + friend class Acceptor; + friend class Writer; + +public: + Receiver (Acceptor *acceptor = 0, int index = -1); + virtual ~Receiver (void); + + //FUZZ: disable check_for_lack_ACE_OS + /// This is called after the new connection has been accepted. + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + //FUZZ: enable check_for_lack_ACE_OS + +protected: + /// This is called by the framework when asynchronous <read> operation from the + /// socket completes. + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + +private: + int initiate_read_stream (void); + + void check_destroy (void); + + Acceptor *acceptor_; + int index_; + + // Socket input + ACE_Asynch_Read_Stream rs_; + ACE_HANDLE socket_handle_; + + // Writer + static Writer* writer_; + + long io_count_; + + char odd_; + + // if we get non-page-size reminder, we will not send it to the writer + // until it is full (unless at end) + ACE_Message_Block *partial_chunk_; +}; + +class Writer : public ACE_Handler +{ + friend class Receiver; + +public: + Writer (void); + virtual ~Writer (void); + + //FUZZ: disable check_for_lack_ACE_OS + void open (void); + //FUZZ: enable check_for_lack_ACE_OS + + // this is *not* a callback from the framework + int handle_read_chunks_chain (ACE_Message_Block *mb, + int type); + + // for determining when last receiver dies + void on_new_receiver (); + void on_delete_receiver (); + +protected: + /// This is called by the framework when an asynchronous <write> to the file + /// completes. + virtual void handle_write_file (const ACE_Asynch_Write_File::Result &result); + +private: + int initiate_write_file (void); + +private: + // Output file + ACE_Asynch_Write_File wf_; + ACE_HANDLE output_file_handle_; + u_long writing_file_offset_; + u_long reported_file_offset_; + ACE_Message_Block *odd_chain_; + ACE_Message_Block *even_chain_; + long io_count_; + char receiver_count_; +}; + +// ************************************************************* +// Receiver Impl +// ************************************************************* + +Writer *Receiver::writer_ = 0; + +Receiver::Receiver (Acceptor * acceptor, int index) + : acceptor_ (acceptor), + index_ (index), + socket_handle_ (ACE_INVALID_HANDLE), + io_count_ (0), + partial_chunk_ (0) +{ + // the first one is the odd one + this->odd_ = ((0 == index) ? 1 : 0); + + if (this->odd_) + { + Receiver::writer_ = new Writer; + if (!Receiver::writer_) + { + ACE_ASSERT (0); + return; + } + } + + Receiver::writer_->on_new_receiver (); + + if (this->acceptor_ != 0) + this->acceptor_->on_new_receiver (*this); +} + +Receiver::~Receiver (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Receiver::~Receiver\n"))); + + if (this->acceptor_ != 0) + this->acceptor_->on_delete_receiver (*this); + + if (this->socket_handle_ != ACE_INVALID_HANDLE) + ACE_OS::closesocket (this->socket_handle_); + + Receiver::writer_->on_delete_receiver (); + + if (this->partial_chunk_) + { + ACE_ASSERT (0); // should not be getting here + this->partial_chunk_->release (); + } +} + +void +Receiver::check_destroy (void) +{ + if (this->io_count_ <= 0) + delete this; +} + +void +Receiver::open (ACE_HANDLE handle, ACE_Message_Block &) +{ + this->socket_handle_ = handle; + + // Open the ACE_Asynch_Read_Stream + if (this->rs_.open (*this, this->socket_handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Receiver::ACE_Asynch_Read_Stream::open"))); + else + { + if (this->odd_) + Receiver::writer_->open (); + + this->initiate_read_stream (); + } + + this->check_destroy (); +} + +int +Receiver::initiate_read_stream (void) +{ + if (!Receiver::writer_) + return -1; + + // how many chunks to allocate? + size_t number_of_new_chunks = (this->partial_chunk_ ? + (ACE_IOV_MAX / RECEIVERS) - 1 + : ACE_IOV_MAX / RECEIVERS); + + // allocate chunks chain + ACE_Message_Block *head_mb = 0; + if (-1 == allocate_chunks_chain (head_mb, number_of_new_chunks)) + { + ACE_ASSERT (0); + return -1; + } + + // calculate how many bytes to read + + // head_mb could be 0 (no new chunks allocated) + size_t bytes_to_read = head_mb ? head_mb->total_size () : 0; + + // add the partial chunk at the front if appropriate, and update + // the number of bytes to read + if (this->partial_chunk_) + { + bytes_to_read += this->partial_chunk_->space (); + this->partial_chunk_->cont (head_mb); + head_mb = this->partial_chunk_; + this->partial_chunk_ = 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Receiver::initiate_read_stream - (%s) readv %d\n"), + this->odd_ ? ACE_TEXT ("ODD ") : ACE_TEXT ("EVEN"), + bytes_to_read)); + + // perform the actual scattered read + if (this->rs_.readv (*head_mb, + bytes_to_read) == -1) + { + free_chunks_chain (head_mb); + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Receiver::ACE_Asynch_Stream::read")), + -1); + } + + ++this->io_count_; + return 0; +} + +void +Receiver::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_Message_Block *mb = &result.message_block (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Receiver::handle_read_stream - (%s) read %d\n"), + this->odd_ ? ACE_TEXT ("ODD ") : ACE_TEXT ("EVEN"), + result.bytes_transferred ())); + + // Transfer only complete chunks to the writer. + // Save last partial chunk for the next call. + // On disconnect (error or 0 transferred), transfer whatever we have. + + // at this stage there should not be anything there + ACE_ASSERT (!this->partial_chunk_); + + // first, remove the empty chunks + remove_empty_chunks (mb); + + if (mb && Receiver::writer_) + { // there's something to write, and who to write to + + // write everything or only complete chunks? + + // write everything - when no new bytes were transferred + int write_everything = 0; + if (!result.bytes_transferred ()) + write_everything = 1; + if (write_everything) + Receiver::writer_->handle_read_chunks_chain (mb, + this->odd_ ? ODD : EVEN); + else + { // filter out the partial chunk at the end (if present) + // and save it for later before writing the full chunks + + // have this->partial_chunk_ point to the last chunk in the chain + size_t last_index = last_chunk (mb, this->partial_chunk_); + if (this->partial_chunk_ && + this->partial_chunk_->length () < chunk_size) + { // found partial chunk at end of chain + // detach it from the chain + if (last_index > 1) // chain bigger than 1 + { + ACE_Message_Block *pre_last = mb; + for (size_t index = 1; index < last_index - 1; ++index) + pre_last = pre_last->cont (); + + // detach partial chunk from chain + pre_last->cont (0); + } + else + // chain in length of 1 - so we need to zero mb + mb = 0; + } + else // last is a full chunk, so hand it over with the rest + this->partial_chunk_ = 0; + + // transfer (if there's anything left) + if (mb && mb->total_length ()) + Receiver::writer_->handle_read_chunks_chain ( + mb, + this->odd_ ? ODD : EVEN); + + // initiate more reads only if no error + if (!result.error ()) + this->initiate_read_stream (); + else + ACE_ASSERT (0); + } + } + else if (mb && !Receiver::writer_) + // no one to write to + free_chunks_chain (mb); + + --this->io_count_; + + this->check_destroy (); +} + +// ************************************************************* +// Acceptor Impl +// ************************************************************* + +Acceptor::Acceptor (void) + : sessions_ (0) +{ + for (int i = 0; i < RECEIVERS; ++i) + this->list_receivers_[i] = 0; +} + +Acceptor::~Acceptor (void) +{ + this->stop (); +} + + +void +Acceptor::stop (void) +{ + // This method can be called only after proactor event loop is done + // in all threads. + for (int i = 0; i < RECEIVERS; ++i) + { + delete this->list_receivers_[i]; + this->list_receivers_[i] = 0; + } +} + +void +Acceptor::on_new_receiver (Receiver & rcvr) +{ + ++this->sessions_; + this->list_receivers_[rcvr.index_] = &rcvr; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Receiver::CTOR sessions_ = %d\n"), + this->sessions_)); +} + +void +Acceptor::on_delete_receiver (Receiver & rcvr) +{ + --this->sessions_; + if (rcvr.index_ >= 0 + && rcvr.index_ < RECEIVERS + && this->list_receivers_[rcvr.index_] == &rcvr) + this->list_receivers_[rcvr.index_] = 0; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Receiver::~DTOR sessions_ = %d\n"), + this->sessions_)); +} + +Receiver * +Acceptor::make_handler (void) +{ + if (this->sessions_ >= RECEIVERS) + return 0; + + for (int i = 0; i < RECEIVERS; ++i) + { + if (this->list_receivers_[i] == 0) + { + ACE_NEW_RETURN (this->list_receivers_[i], + Receiver (this, i), + 0); + return this->list_receivers_[i]; + } + } + + return 0; +} + +// ************************************************************* +// Writer Impl +// ************************************************************* + +Writer::Writer (void) +: output_file_handle_ (ACE_INVALID_HANDLE), + writing_file_offset_ (0), + reported_file_offset_ (0), + odd_chain_ (0), + even_chain_ (0), + io_count_ (0), + receiver_count_ (0) +{ +} + +Writer::~Writer (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::~Writer\n"))); + + if (this->output_file_handle_ != ACE_INVALID_HANDLE) + ACE_OS::close (this->output_file_handle_); + + Receiver::writer_ = 0; +} + +void +Writer::on_new_receiver () +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::on_new_receiver\n"))); + + ++this->receiver_count_; +} + +void +Writer::on_delete_receiver () +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::on_delete_receiver\n"))); + + --this->receiver_count_; + + if (0 == this->receiver_count_) + { + if (this->io_count_ <= 0) + // no pending io, so do the work oursleves + // (if pending io, they'll see the zero receiver count) + this->initiate_write_file (); + } +} + +void +Writer::open (void) +{ + // Open the file for output + if (ACE_INVALID_HANDLE == (this->output_file_handle_ = ACE_OS::open (output_file, + O_CREAT | _O_TRUNC | _O_WRONLY |\ + FILE_FLAG_OVERLAPPED |\ + FILE_FLAG_NO_BUFFERING, + ACE_DEFAULT_FILE_PERMS))) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Writer::open::ACE_OS::open"))); + // Open the ACE_Asynch_Write_File + else if (this->wf_.open (*this, this->output_file_handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Writer::open::ACE_Asynch_Write_File::open"))); +} + +int +Writer::handle_read_chunks_chain (ACE_Message_Block *mb, + int type) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::handle_read_chunks_chain - (%s) %d bytes\n"), + (type == ODD) ? ACE_TEXT ("ODD ") : ACE_TEXT ("EVEN"), + mb->total_length ())); + + add_to_chunks_chain (ODD == type ? this->odd_chain_ : this->even_chain_, mb); + + this->initiate_write_file (); + + return 0; +} + +int +Writer::initiate_write_file (void) +{ + // find out how much can we merge + ACE_Message_Block *dummy_last = 0; + size_t odd_count = last_chunk (this->odd_chain_, dummy_last); + size_t even_count = last_chunk (this->even_chain_, dummy_last); + + size_t merge_size = ACE_MIN (ACE_MIN (odd_count, even_count), + (size_t) ACE_IOV_MAX); + + // the options here are as follows: + // io_count_ can be zero or greater. + // merge_size can be zero or not. + // if non zero merge, write the merge. ASSERT receiver_count_ is non zero too. + // if zero merge: + // if receiver_count_ is non zero, NOOP. + // if zero receiver_count_, we should write whatever is left, + // and terminate the writer at completion. + // if nothing to write, and io_count_ is zero too, terminate here. + + if (0 == merge_size && + 0 != this->receiver_count_) + return 0; + + if (0 == merge_size && + 0 == this->receiver_count_ && + 0 == odd_count && + 0 == even_count && + 0 == this->io_count_) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::initiate_write_file") + ACE_TEXT (" - ending proactor event loop\n"))); + + ACE_Proactor::instance ()->end_event_loop (); + + delete this; + + return 0; + } + + // if we reached nere and merge_size is zero, we should write whatever is + // in the queues (1 to 2 chunks together), so let's force the merge size to 1. + if (0 == merge_size) + { + ACE_ASSERT (1 == odd_count && 1 >= even_count); + merge_size = 1; + } + + // Now that we found out what we want to do, prepare the chain + // that will be written, and update the remainders + ACE_Message_Block *new_odd_chain_head = this->odd_chain_; + ACE_Message_Block *new_even_chain_head = this->even_chain_; + + // locate the place for detachment in the chains + ACE_Message_Block *pre_odd = 0; + ACE_Message_Block *pre_even = 0; + for (size_t index = 0; index < merge_size; ++index) + { + pre_odd = new_odd_chain_head; + if (new_odd_chain_head) + new_odd_chain_head = new_odd_chain_head->cont (); + pre_even = new_even_chain_head; + if (new_even_chain_head) + new_even_chain_head = new_even_chain_head->cont (); + } + // now detach the chain + if (pre_odd) + pre_odd->cont (0); + if (pre_even) + pre_even->cont (0); + + // perform merge between the two chains + merge_odd_even_chains (this->odd_chain_, this->even_chain_); + + // and now finally perform the write + ACE_Message_Block *united_mb = this->odd_chain_; + // update the remainders of the chains + this->odd_chain_ = new_odd_chain_head; + this->even_chain_ = new_even_chain_head; + size_t increment_writing_file_offset = united_mb->total_length (); + + // Reconstruct the file + // Write the size, not the length, because we must write in chunks + // of <page size> + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::initiate_write_file: write %d bytes at %d\n"), + united_mb->total_size (), + this->writing_file_offset_)); + if (this->wf_.writev (*united_mb, + united_mb->total_size (), + this->writing_file_offset_) == -1) + { + free_chunks_chain (united_mb); + + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Writer::initiate_write_file::ACE_Asynch_Write_Stream::writev")), + -1); + } + + // we update now because otherwise, we'd have error when performing + // pipelined writing (that is, mulitple calls to write before the callbacks + // to handle_x) + this->writing_file_offset_ += + static_cast<u_long> (increment_writing_file_offset); + ++this->io_count_; + return 0; +} + +void +Writer::handle_write_file (const ACE_Asynch_Write_File::Result &result) +{ + ACE_Message_Block *mb = &result.message_block (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::handle_write_file at offset %d wrote %d\n"), + this->reported_file_offset_, + result.bytes_transferred ())); + + this->reported_file_offset_ += + static_cast<u_long> (result.bytes_transferred ()); + + // Always truncate as required, + // because partial will always be the last write to a file + ACE_Message_Block *last_mb = mb; + last_chunk (mb, last_mb); + + if (last_mb->space ()) + ACE_OS::truncate (output_file, + this->reported_file_offset_ - + static_cast<u_long> (last_mb->space ())); + + free_chunks_chain (mb); + + --this->io_count_; + + // end of process? + if (0 == this->receiver_count_ && + 0 == this->io_count_) + { + ACE_ASSERT (0 == this->odd_chain_ && 0 == this->even_chain_); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Writer::handle_write_file") + ACE_TEXT (" - ending proactor event loop\n"))); + + ACE_Proactor::instance ()->end_event_loop (); + + delete this; + } +} + +// ************************************************************* +// Connector and Sender +// ************************************************************* +class Sender; + +class Connector : public ACE_Asynch_Connector<Sender> +{ + friend class Sender; + +public: + Connector (void); + virtual ~Connector (void); + + // Address to pass to Sender for secondary connect. + void set_address (const ACE_INET_Addr &addr); + const ACE_INET_Addr &get_address (void); + + void stop (void); + + // Virtual from ACE_Asynch_Connector + virtual Sender *make_handler (void); + +private: + void on_new_sender (Sender &rcvr); + void on_delete_sender (Sender &rcvr); + + int sessions_; + ACE_INET_Addr addr_; + Sender *list_senders_[SENDERS]; +}; + +class Sender : public ACE_Service_Handler +{ + friend class Connector; +public: + + Sender (Connector *connector = 0, int index = -1); + + virtual ~Sender (void); + + //FUZZ: disable check_for_lack_ACE_OS + /// This is called after the new connection has been established. + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + //FUZZ: enable check_for_lack_ACE_OS + + // This is called by the framework when asynchronous reads from the + // file complete. + virtual void handle_read_file (const ACE_Asynch_Read_File::Result &result); + + // This is called by the framework when asynchronous writes from the + // socket complete. + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + +private: + void check_destroy (void); + + int initiate_read_file (void); + + int initiate_write_stream (ACE_Message_Block &mb); + + int index_; + Connector * connector_; + + // File to read from + ACE_Asynch_Read_File rf_; + ACE_HANDLE input_file_handle_; + u_long file_offset_; + + // Sockets to send to + // odd and even socket output streams + ACE_Asynch_Write_Stream ws_[RECEIVERS]; + ACE_HANDLE socket_handle_[RECEIVERS]; + + long io_count_; +}; + +// ************************************************************* +// Connector Impl +// ************************************************************* + +Connector::Connector (void) + : sessions_ (0) +{ + for (int i = 0; i < SENDERS; ++i) + this->list_senders_[i] = 0; +} + +Connector::~Connector (void) +{ + this->stop (); +} + +// Address to pass to Sender for secondary connect. +void +Connector::set_address (const ACE_INET_Addr &addr) +{ + this->addr_ = addr; +} + +const ACE_INET_Addr & +Connector::get_address (void) +{ + return this->addr_; +} + +void +Connector::stop (void) +{ + // This method can be called only after proactor event loop is done + // in all threads. + + for (int i = 0; i < SENDERS; ++i) + { + delete this->list_senders_[i]; + this->list_senders_[i] = 0; + } +} + +void +Connector::on_new_sender (Sender &sndr) +{ + ++this->sessions_; + this->list_senders_[sndr.index_] = &sndr; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::CTOR sessions_ = %d\n"), + this->sessions_)); +} + +void +Connector::on_delete_sender (Sender &sndr) +{ + --this->sessions_; + if (sndr.index_ >= 0 + && sndr.index_ < SENDERS + && this->list_senders_[sndr.index_] == &sndr) + this->list_senders_[sndr.index_] = 0; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::~DTOR sessions_ = %d\n"), + this->sessions_)); +} + +Sender * +Connector::make_handler (void) +{ + if (this->sessions_ >= SENDERS) + return 0; + + for (int i = 0; i < SENDERS; ++i) + { + if (this->list_senders_ [i] == 0) + { + ACE_NEW_RETURN (this->list_senders_[i], + Sender (this, i), + 0); + return this->list_senders_[i]; + } + } + + return 0; +} + +// ************************************************************* +// Sender Impl +// ************************************************************* + +Sender::Sender (Connector * connector, int index) + : index_ (index), + connector_ (connector), + input_file_handle_ (ACE_INVALID_HANDLE), + file_offset_ (0), + io_count_ (0) +{ + socket_handle_[ODD] = socket_handle_[EVEN] = ACE_INVALID_HANDLE; + + if (this->connector_ != 0) + this->connector_->on_new_sender (*this); +} + +Sender::~Sender (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::~Sender\n"))); + + if (this->connector_ != 0) + this->connector_->on_delete_sender (*this); + + if (this->socket_handle_[ODD] != ACE_INVALID_HANDLE) + ACE_OS::closesocket (this->socket_handle_[ODD]); + + if (this->socket_handle_[EVEN] != ACE_INVALID_HANDLE) + ACE_OS::closesocket (this->socket_handle_[EVEN]); + + if (this->input_file_handle_ != ACE_INVALID_HANDLE) + ACE_OS::close (this->input_file_handle_); + + if (client_only) + ACE_Proactor::instance ()->end_event_loop (); +} + +// return true if we alive, false we commited suicide +void +Sender::check_destroy (void) +{ + if (this->io_count_ <= 0) + delete this; +} + +void +Sender::open (ACE_HANDLE handle, ACE_Message_Block &) +{ + this->socket_handle_[ODD] = handle; + + // Open the input file + if (ACE_INVALID_HANDLE == (this->input_file_handle_ = + ACE_OS::open (input_file, + _O_RDONLY |\ + FILE_FLAG_OVERLAPPED |\ + FILE_FLAG_NO_BUFFERING, + ACE_DEFAULT_FILE_PERMS))) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::open::ACE_OS::open"))); + } + else + { + // Now connect (w/o the connector factory) to the even (=second) + // receiver. We don't connect thru the factory in order not to + // instantiate another Sender. + ACE_SOCK_Connector sock_connector; + ACE_SOCK_Stream sock_stream; + if (-1 == sock_connector.connect (sock_stream, + this->connector_->get_address ())) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::open::ACE_SOCK_Connector::connect"))); + + else + { + this->socket_handle_[EVEN] = sock_stream.get_handle (); + + // Open odd ACE_Asynch_Write_Stream + if (this->ws_[ODD].open (*this, this->socket_handle_[ODD]) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::open::ACE_Asynch_Write_Stream::open"))); + + // Open even ACE_Asynch_Write_Stream + else if (this->ws_[EVEN].open (*this, this->socket_handle_[EVEN]) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::open::ACE_Asynch_Write_Stream::open"))); + + // Open ACE_Asynch_Read_File + else if (this->rf_.open (*this, this->input_file_handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::open::ACE_Asynch_Read_File::open"))); + else + // Start an asynchronous read + this->initiate_read_file (); + } + } + + this->check_destroy (); +} + +int +Sender::initiate_read_file (void) +{ + ACE_ASSERT (0 == this->file_offset_ % chunk_size); + + static const size_t file_size = ACE_OS::filesize (input_file); + + static const size_t number_of_chunks_needed_for_file = + static_cast<size_t> (ACE_OS::ceil ((double) file_size / chunk_size)); + + size_t relevant_number_of_chunks = + ACE_MIN ((size_t)ACE_IOV_MAX, + number_of_chunks_needed_for_file + - (size_t)(this->file_offset_ / chunk_size)); + + if (!relevant_number_of_chunks) + { + ACE_ASSERT (0); // Just 2 C it coming + return 0; + } + + ACE_Message_Block *head_mb = 0; + if (-1 == allocate_chunks_chain (head_mb, relevant_number_of_chunks)) + { + ACE_ASSERT (0); + return -1; + } + + // Inititiate read + if (this->rf_.readv (*head_mb, + head_mb->total_size (), + this->file_offset_) == -1) + { + free_chunks_chain (head_mb); + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::initiate_read_file::") + ACE_TEXT ("ACE_Asynch_Read_Stream::readv")), + -1); + } + + ++this->io_count_; + return 0; +} + +int +Sender::initiate_write_stream (ACE_Message_Block &mb) +{ + // send the odd to the first connection, and the even to the second + // connection. + + ACE_Message_Block *odd_mb = &mb; + ACE_Message_Block *even_mb = mb.cont (); + + split_odd_even_chains (odd_mb, even_mb); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::initiate_write_stream - (ODD ) writev %d\n"), + odd_mb->total_length ())); + + if (this->ws_[ODD].writev (*odd_mb, odd_mb->total_length ()) == -1) + { + free_chunks_chain (odd_mb); + + if (even_mb) + free_chunks_chain (even_mb); + + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::ACE_Asynch_Stream::writev")), + -1); + } + + ++this->io_count_; + + if (even_mb) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::initiate_write_stream - (EVEN) writev %d\n"), + even_mb->total_length ())); + + if (this->ws_[EVEN].writev (*even_mb, even_mb->total_length ()) == -1) + { + free_chunks_chain (even_mb); + + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Sender::ACE_Asynch_Stream::writev")), + -1); + } + + ++this->io_count_; + } + + return 0; +} + +void +Sender::handle_read_file (const ACE_Asynch_Read_File::Result &result) +{ + ACE_Message_Block *mb = &result.message_block (); + + if (result.error () == 0 && result.bytes_transferred () != 0) + { + size_t bytes_transferred = result.bytes_transferred (); + size_t chunks_chain_size = mb->total_size (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::handle_read_file, read %d, ") + ACE_TEXT ("chain total %d\n"), + bytes_transferred, + chunks_chain_size)); + + this->file_offset_ += static_cast<u_long> (bytes_transferred); + + this->initiate_write_stream (*mb); + + // and read more if required + if (bytes_transferred == chunks_chain_size) + this->initiate_read_file (); + } + else + free_chunks_chain (mb); + + --this->io_count_; + + this->check_destroy (); +} + +void +Sender::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + ACE_Message_Block *mb = &result.message_block (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::handle_write_stream - wrote %d bytes\n"), + result.bytes_transferred ())); + + if (result.error () == 0 && result.bytes_transferred () != 0) + // verify sent all + ACE_ASSERT (0 == mb->total_length ()); + else + ACE_ASSERT (0); + + free_chunks_chain (mb); + + --this->io_count_; + + this->check_destroy (); +} + +// ************************************************************* +// Configuration helpers +// ************************************************************* +int +print_usage (int /* argc */, ACE_TCHAR *argv[]) +{ + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("\nusage: %s") + ACE_TEXT ("\n-f <input file>\n") + ACE_TEXT ("\n-c client only (reader-sender)") + ACE_TEXT ("\n-s server only (receiver-writer)") + ACE_TEXT ("\n-h host to connect to") + ACE_TEXT ("\n-p port") + ACE_TEXT ("\n-u show this message") + ACE_TEXT ("\n"), + argv[0] + )); + return -1; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + if (argc == 1) // no arguments , so one button test + return 0; + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("f:csh:p:u")); + int c; + + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'f': + input_file = get_opt.opt_arg (); + break; + case 'c': + client_only = 1; + server_only = 0; + break; + case 's': + server_only = 1; + client_only = 0; + break; + case 'h': + host = get_opt.opt_arg (); + break; + case 'p': + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'u': + default: + return print_usage (argc, argv); + } // switch + } // while + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Scatter_Gather_Test")); + + if (::parse_args (argc, argv) == -1) + return -1; + + chunk_size = ACE_OS::getpagesize (); + + if (client_only) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running as client only, page size %d\n"), + chunk_size)); + else if (server_only) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running as server only, page size %d\n"), + chunk_size)); + else + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Running as server and client, page size %d\n"), + chunk_size)); + + Acceptor acceptor; + Connector connector; + ACE_INET_Addr addr (port); + + if (!client_only) + { + // Simplify, initial read with zero size + if (-1 == acceptor.open (addr, 0, 1)) + { + ACE_ASSERT (0); + return -1; + } + } + + if (!server_only) + { + if (-1 == connector.open (1, ACE_Proactor::instance ())) + { + ACE_ASSERT (0); + return -1; + } + + // connect to first destination + if (addr.set (port, host, 1, addr.get_type ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), host), -1); + connector.set_address (addr); + if (-1 == connector.connect (addr)) + { + ACE_ASSERT (0); + return -1; + } + } + + ACE_Proactor::instance ()->run_event_loop (); + + // As Proactor event loop now is inactive it is safe to destroy all + // senders + + connector.stop (); + acceptor.stop (); + + ACE_Proactor::instance()->close_singleton (); + + // now compare the files - available only when on same machine + + int success = 0; + if (!client_only && !server_only) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Comparing the input file and the output file...\n"))); + + success = -1; + // map the two files, then perform memcmp + { + ACE_Mem_Map original_file (input_file); + ACE_Mem_Map reconstructed_file (output_file); + + if (original_file.addr () && + original_file.addr () != MAP_FAILED && + reconstructed_file.addr () && + reconstructed_file.addr () != MAP_FAILED) + { + // compare lengths + if ((original_file.size () == reconstructed_file.size ()) && + // and if same size, compare file data + (0 == ACE_OS::memcmp (original_file.addr (), + reconstructed_file.addr (), + original_file.size ()))) + success = 0; + } + } + + if (0 == success) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("input file and the output file identical!\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("input file and the output file are different!\n"))); + } + + if (!client_only) + ACE_OS::unlink (output_file); + + ACE_END_TEST; + + return success; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Scatter_Gather_Test")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Asynchronous Scatter/Gather IO is unsupported.\n") + ACE_TEXT ("Proactor_Scatter_Gather_Test will not be run."))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_WIN32_OVERLAPPED_IO */ diff --git a/ACE/tests/Proactor_Test.cpp b/ACE/tests/Proactor_Test.cpp new file mode 100644 index 00000000000..861def76e50 --- /dev/null +++ b/ACE/tests/Proactor_Test.cpp @@ -0,0 +1,1943 @@ +// $Id$ + +// ============================================================================ +/** + * @file Proactor_Test.cpp + * + * $Id$ + * + * This program illustrates how the ACE_Proactor can be used to + * implement an application that does various asynchronous + * operations. + * + * @author Alexander Libman <alibman@baltimore.com> + */ +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID (tests, + Proactor_Test, + "$Id$") + +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_WIN32_OVERLAPPED_IO) || defined (ACE_HAS_AIO_CALLS)) + // This only works on Win32 platforms and on Unix platforms + // supporting POSIX aio calls. + +#include "ace/Signal.h" + +#include "ace/Service_Config.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/Object_Manager.h" +#include "ace/Get_Opt.h" + +#include "ace/Proactor.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/Asynch_Connector.h" +#include "ace/Task.h" +#include "ace/Thread_Semaphore.h" +#include "ace/OS_NS_ctype.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_socket.h" +#include "ace/os_include/netinet/os_tcp.h" + +#include "ace/Atomic_Op.h" +#include "ace/Synch_Traits.h" + +#if defined (ACE_WIN32) + +# include "ace/WIN32_Proactor.h" + +#elif defined (ACE_HAS_AIO_CALLS) + +# include "ace/POSIX_Proactor.h" +# include "ace/POSIX_CB_Proactor.h" +# include "ace/SUN_Proactor.h" + +#endif /* ACE_WIN32 */ + +#include "Proactor_Test.h" + + +// Proactor Type (UNIX only, Win32 ignored) +typedef enum { DEFAULT = 0, AIOCB, SIG, SUN, CB } ProactorType; +static ProactorType proactor_type = DEFAULT; + +// POSIX : > 0 max number aio operations proactor, +static size_t max_aio_operations = 0; + +// both: 0 run client or server / depends on host +// != 0 run client and server +static int both = 0; + +// Host that we're connecting to. +static const ACE_TCHAR *host = 0; + +// number of Client instances +static int clients = 1; +const int MAX_CLIENTS = 1000; +const int MAX_SERVERS = 1000; + +// duplex mode: == 0 half-duplex +// != 0 full duplex +static int duplex = 0; + +// number threads in the Proactor thread pool +static int threads = 1; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +// Log options +static int loglevel; // 0 full , 1 only errors + +static size_t xfer_limit; // Number of bytes for Client to send. + +static char complete_message[] = + "GET / HTTP/1.1\r\n" + "Accept: */*\r\n" + "Accept-Language: C++\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "User-Agent: Proactor_Test/1.0 (non-compatible)\r\n" + "Connection: Keep-Alive\r\n" + "\r\n"; + +class LogLocker +{ +public: + + LogLocker () { ACE_LOG_MSG->acquire (); } + virtual ~LogLocker () { ACE_LOG_MSG->release (); } +}; + + + +// Function to remove signals from the signal mask. +static int +disable_signal (int sigmin, int sigmax) +{ +#ifndef ACE_WIN32 + + sigset_t signal_set; + if (ACE_OS::sigemptyset (&signal_set) == - 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: (%P|%t):%p\n"), + ACE_TEXT ("sigemptyset failed"))); + + for (int i = sigmin; i <= sigmax; i++) + ACE_OS::sigaddset (&signal_set, i); + + // Put the <signal_set>. + if (ACE_OS::pthread_sigmask (SIG_BLOCK, &signal_set, 0) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: (%P|%t):%p\n"), + ACE_TEXT ("pthread_sigmask failed"))); +#else + ACE_UNUSED_ARG (sigmin); + ACE_UNUSED_ARG (sigmax); +#endif /* ACE_WIN32 */ + + return 1; +} + + +// ************************************************************* +// MyTask is ACE_Task resposible for : +// 1. creation and deletion of +// Proactor and Proactor thread pool +// 2. running Proactor event loop +// ************************************************************* + +/** + * @class MyTask + * + * MyTask plays role for Proactor threads pool + * + * MyTask is ACE_Task resposible for: + * 1. Creation and deletion of Proactor and Proactor thread pool + * 2. Running Proactor event loop + */ +class MyTask : public ACE_Task<ACE_MT_SYNCH> +{ +public: + MyTask (void): + lock_ (), + sem_ ((unsigned int) 0), + proactor_(0) {} + + virtual ~MyTask() + { + (void) this->stop (); + (void) this->delete_proactor(); + } + + virtual int svc (void); + + int start (int num_threads, + ProactorType type_proactor, + size_t max_op ); + int stop (void); + +private: + int create_proactor (ProactorType type_proactor, + size_t max_op); + int delete_proactor (void); + + ACE_SYNCH_RECURSIVE_MUTEX lock_; + ACE_Thread_Semaphore sem_; + ACE_Proactor * proactor_; + +}; + +int +MyTask::create_proactor (ProactorType type_proactor, size_t max_op) +{ + ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + monitor, + this->lock_, + -1); + + ACE_ASSERT (this->proactor_ == 0); + +#if defined (ACE_WIN32) + + ACE_UNUSED_ARG (type_proactor); + ACE_UNUSED_ARG (max_op); + + ACE_WIN32_Proactor *proactor_impl = 0; + + ACE_NEW_RETURN (proactor_impl, + ACE_WIN32_Proactor, + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Create Proactor Type = WIN32\n"))); + +#elif defined (ACE_HAS_AIO_CALLS) + + ACE_POSIX_Proactor * proactor_impl = 0; + + switch (type_proactor) + { + case AIOCB: + ACE_NEW_RETURN (proactor_impl, + ACE_POSIX_AIOCB_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = AIOCB\n"))); + break; + +#if defined(ACE_HAS_POSIX_REALTIME_SIGNALS) + case SIG: + ACE_NEW_RETURN (proactor_impl, + ACE_POSIX_SIG_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = SIG\n"))); + break; +#endif /* ACE_HAS_POSIX_REALTIME_SIGNALS */ + +# if defined (sun) + case SUN: + ACE_NEW_RETURN (proactor_impl, + ACE_SUN_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Create Proactor Type = SUN\n"))); + break; +# endif /* sun */ + +# if !defined(ACE_HAS_BROKEN_SIGEVENT_STRUCT) + case CB: + ACE_NEW_RETURN (proactor_impl, + ACE_POSIX_CB_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = CB\n"))); + break; +# endif /* !ACE_HAS_BROKEN_SIGEVENT_STRUCT */ + + default: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = DEFAULT\n"))); + break; + } + +#endif /* ACE_WIN32 */ + + // always delete implementation 1 , not !(proactor_impl == 0) + ACE_NEW_RETURN (this->proactor_, + ACE_Proactor (proactor_impl, 1 ), + -1); + // Set new singleton and delete it in close_singleton() + ACE_Proactor::instance (this->proactor_, 1); + return 0; +} + +int +MyTask::delete_proactor (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + monitor, + this->lock_, + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Delete Proactor\n"))); + + ACE_Proactor::close_singleton (); + this->proactor_ = 0; + + return 0; +} + +int +MyTask::start (int num_threads, + ProactorType type_proactor, + size_t max_op) +{ + if (this->create_proactor (type_proactor, max_op) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to create proactor")), + -1); + + if (this->activate (THR_NEW_LWP, num_threads) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to activate thread pool")), + -1); + + for (; num_threads > 0; num_threads--) + { + sem_.acquire (); + } + + return 0; +} + + +int +MyTask::stop () +{ + if (this->proactor_ != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Calling End Proactor event loop\n"))); + + this->proactor_->proactor_end_event_loop (); + } + + if (this->wait () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to stop thread pool"))); + + return 0; +} + +int +MyTask::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) MyTask started\n"))); + + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + + // signal that we are ready + sem_.release (1); + + ACE_Proactor::run_event_loop (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) MyTask finished\n"))); + return 0; +} + + +// TestData collects and reports on test-related transfer and connection +// statistics. +class TestData +{ +public: + TestData (); + bool testing_done (void); + Server *server_up (void); + Client *client_up (void); + void server_done (Server *s); + void client_done (Client *c); + void stop_all (void); + void report (void); + +private: + struct Local_Stats + { + // Track number of sessions that report start, and those that report + // their end (and stats). + ACE_Atomic_Op<ACE_SYNCH_MUTEX, int> sessions_up_; + ACE_Atomic_Op<ACE_SYNCH_MUTEX, int> sessions_down_; + + // Total read and write bytes for all sessions. + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> w_cnt_; + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> r_cnt_; + // Total read and write operations issues for all sessions. + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> w_ops_; + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> r_ops_; + } servers_, clients_; + + ACE_SYNCH_MUTEX list_lock_; + Server *server_list_[MAX_SERVERS]; + Client *client_list_[MAX_CLIENTS]; +}; + +TestData::TestData () +{ + int i; + for (i = 0; i < MAX_SERVERS; ++i) + this->server_list_[i] = 0; + for (i = 0; i < MAX_CLIENTS; ++i) + this->client_list_[i] = 0; +} + +bool +TestData::testing_done (void) +{ + int svr_up = this->servers_.sessions_up_.value (); + int svr_dn = this->servers_.sessions_down_.value (); + int clt_up = this->clients_.sessions_up_.value (); + int clt_dn = this->clients_.sessions_down_.value (); + + if (svr_up == 0 && clt_up == 0) // No connections up yet + return false; + + return (svr_dn >= svr_up && clt_dn >= clt_up); +} + +Server * +TestData::server_up (void) +{ + ++this->servers_.sessions_up_; + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, monitor, this->list_lock_, 0); + + for (int i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] == 0) + { + ACE_NEW_RETURN (this->server_list_[i], Server (this, i), 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d up; now %d up, %d down.\n"), + i, + this->servers_.sessions_up_.value (), + this->servers_.sessions_down_.value ())); + return this->server_list_[i]; + } + } + return 0; +} + +Client * +TestData::client_up (void) +{ + ++this->clients_.sessions_up_; + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, monitor, this->list_lock_, 0); + + for (int i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] == 0) + { + ACE_NEW_RETURN (this->client_list_[i], Client (this, i), 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d up; now %d up, %d down.\n"), + i, + this->clients_.sessions_up_.value (), + this->clients_.sessions_down_.value ())); + return this->client_list_[i]; + } + } + return 0; +} + +void +TestData::server_done (Server *s) +{ + this->servers_.w_cnt_ += s->get_total_snd (); + this->servers_.r_cnt_ += s->get_total_rcv (); + this->servers_.w_ops_ += s->get_total_w (); + this->servers_.r_ops_ += s->get_total_r (); + ++this->servers_.sessions_down_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d gone; now %d up, %d down\n"), + s->id (), + this->servers_.sessions_up_.value (), + this->servers_.sessions_down_.value ())); + + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + int i; + for (i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] == s) + { + if (s->id () != i) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Server %d is pos %d in list\n"), + s->id (), + i)); + this->server_list_[i] = 0; + break; + } + } + if (i >= MAX_SERVERS) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Server %@ done but not listed\n"), s)); + + return; +} + +void +TestData::client_done (Client *c) +{ + this->clients_.w_cnt_ += c->get_total_snd (); + this->clients_.r_cnt_ += c->get_total_rcv (); + this->clients_.w_ops_ += c->get_total_w (); + this->clients_.r_ops_ += c->get_total_r (); + ++this->clients_.sessions_down_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d gone; now %d up, %d down\n"), + c->id (), + this->clients_.sessions_up_.value (), + this->clients_.sessions_down_.value ())); + + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + int i; + for (i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] == c) + { + if (c->id () != i) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Client %d is pos %d in list\n"), + c->id (), + i)); + this->client_list_[i] = 0; + break; + } + } + if (i >= MAX_CLIENTS) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Client %@ done but not listed\n"), c)); + + return; +} + +void +TestData::stop_all (void) +{ + int i; + + // Lock and cancel everything. Then release the lock, possibly allowing + // cleanups, then grab it again and delete all Servers and Clients. + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + for (i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] != 0) + this->client_list_[i]->cancel (); + } + + for (i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] != 0) + this->server_list_[i]->cancel (); + } + } + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + for (i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] != 0) + delete this->client_list_[i]; + } + + for (i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] != 0) + delete this->server_list_[i]; + } + } +} + +void +TestData::report (void) +{ + // Print statistics + ACE_TCHAR bufs [256]; + ACE_TCHAR bufr [256]; + + ACE_OS::sprintf (bufs, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->clients_.w_cnt_.value (), + this->clients_.w_ops_.value ()); + + ACE_OS::sprintf (bufr, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->clients_.r_cnt_.value (), + this->clients_.r_ops_.value ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Clients total bytes (ops): snd=%s rcv=%s\n"), + bufs, + bufr)); + + ACE_OS::sprintf (bufs, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->servers_.w_cnt_.value (), + this->servers_.w_ops_.value ()); + + ACE_OS::sprintf (bufr, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->servers_.r_cnt_.value (), + this->servers_.r_ops_.value ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Servers total bytes (ops): snd=%s rcv=%s\n"), + bufs, + bufr)); + + if (this->clients_.w_cnt_.value () == 0 || + this->clients_.r_cnt_.value () == 0 || + this->servers_.w_cnt_.value () == 0 || + this->servers_.r_cnt_.value () == 0 ) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("It appears that this test didn't ") + ACE_TEXT ("really do anything. Something is very wrong.\n"))); +} + + +class Acceptor : public ACE_Asynch_Acceptor<Server> +{ +public: + Acceptor (TestData *tester); + virtual ~Acceptor (void); + + // Virtual from ACE_Asynch_Acceptor + Server *make_handler (void); + +private: + TestData *tester_; +}; + +// ************************************************************* +Acceptor::Acceptor (TestData *tester) + : tester_ (tester) +{ +} + +Acceptor::~Acceptor (void) +{ + this->cancel (); +} + +Server * +Acceptor::make_handler (void) +{ + return this->tester_->server_up (); +} + +// *************************************************** +Server::Server () +{ + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Shouldn't use this constructor!\n"))); +} + +Server::Server (TestData *tester, int id) + : tester_ (tester), + id_ (id), + handle_ (ACE_INVALID_HANDLE), + io_count_ (0), + flg_cancel_(0), + total_snd_(0), + total_rcv_(0), + total_w_ (0), + total_r_ (0) +{ +} + +Server::~Server (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d dtor; %d sends (%B bytes); ") + ACE_TEXT ("%d recvs (%B bytes)\n"), + this->id_, + this->total_w_, this->total_snd_, + this->total_r_, this->total_rcv_)); + if (this->io_count_ != 0) + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("(%t) Server %d deleted with ") + ACE_TEXT ("%d I/O outstanding\n"), + this->id_, + this->io_count_)); + + // This test bounces data back and forth between Clients and Servers. + // Therefore, if there was significantly more data in one direction, that's + // a problem. Remember, the byte counts are unsigned values. + int issue_data_warning = 0; + if (this->total_snd_ > this->total_rcv_) + { + if (this->total_rcv_ == 0) + issue_data_warning = 1; + else if (this->total_snd_ / this->total_rcv_ > 2) + issue_data_warning = 1; + } + else + { + if (this->total_snd_ == 0) + issue_data_warning = 1; + else if (this->total_rcv_ / this->total_snd_ > 2) + issue_data_warning = 1; + } + if (issue_data_warning) + ACE_DEBUG ((LM_WARNING, + ACE_TEXT ("(%t) Above byte counts look odd; need review\n"))); + + if (this->tester_ != 0) + this->tester_->server_done (this); + + if (this->handle_ != ACE_INVALID_HANDLE) + ACE_OS::closesocket (this->handle_); + + this->id_ = -1; + this->handle_= ACE_INVALID_HANDLE; +} + +void +Server::cancel () +{ + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + this->flg_cancel_ = 1; + this->ws_.cancel (); + this->rs_.cancel (); + return; +} + + +void +Server::addresses (const ACE_INET_Addr& peer, const ACE_INET_Addr&) +{ + ACE_TCHAR str[256]; + if (0 == peer.addr_to_string (str, sizeof (str) / sizeof (ACE_TCHAR))) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d connection from %s\n"), + this->id_, + str)); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) Server %d %p\n"), + this->id_, + ACE_TEXT ("addr_to_string"))); + return; +} + + +void +Server::open (ACE_HANDLE handle, ACE_Message_Block &) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + // Don't buffer serial sends. + this->handle_ = handle; + int nodelay = 1; + ACE_SOCK_Stream option_setter (handle); + if (-1 == option_setter.set_option (ACE_IPPROTO_TCP, + TCP_NODELAY, + &nodelay, + sizeof (nodelay))) + ACE_ERROR ((LM_ERROR, "%p\n", "set_option")); + + if (this->ws_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Server::ACE_Asynch_Write_Stream::open"))); + else if (this->rs_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Server::ACE_Asynch_Read_Stream::open"))); + else + this->initiate_read_stream (); + + if (this->io_count_ > 0) + return; + } + delete this; +} + +int +Server::initiate_read_stream (void) +{ + if (this->flg_cancel_ != 0 || this->handle_ == ACE_INVALID_HANDLE) + return -1; + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (1024), //BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size () - 1) == -1) + { + mb->release (); +#if defined (ACE_WIN32) + // On peer close, ReadFile will yield ERROR_NETNAME_DELETED; won't get + // a 0-byte read as we would if underlying calls used WSARecv. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d, peer closed\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Server %d, %p\n"), + this->id_, + ACE_TEXT ("read")), + -1); + } + + this->io_count_++; + this->total_r_++; + return 0; +} + +int +Server::initiate_write_stream (ACE_Message_Block &mb, size_t nbytes) +{ + if (this->flg_cancel_ != 0 || this->handle_ == ACE_INVALID_HANDLE) + { + mb.release (); + return -1; + } + + if (nbytes == 0) + { + mb.release (); + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) Server::ACE_Asynch_Write_Stream::write nbytes <0 ")), + -1); + } + + if (this->ws_.write (mb, nbytes) == -1) + { + mb.release (); +#if defined (ACE_WIN32) + // On peer close, WriteFile will yield ERROR_NETNAME_DELETED. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d, peer gone\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) Server %d, %p\n"), + this->id_, + ACE_TEXT ("write")), + -1); + } + + this->io_count_++; + this->total_w_++; + return 0; +} + +void +Server::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_ ); + + ACE_Message_Block & mb = result.message_block (); + + // Reset pointers. + mb.rd_ptr ()[result.bytes_transferred ()] = '\0'; + + if (loglevel > 1) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Server %d: handle_read_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_to_read"), + result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Server %d; %p\n"), + this->id_, + ACE_TEXT ("read")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d: read %B bytes\n"), + this->id_, + result.bytes_transferred ())); + } + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_rcv_ += result.bytes_transferred (); + + if (this->initiate_write_stream (mb, + result.bytes_transferred ()) == 0) + { + if (duplex != 0) // Initiate new read from the stream. + this->initiate_read_stream (); + } + } + else + mb.release (); + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +void +Server::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + ACE_Message_Block & mb = result.message_block (); + + if (loglevel > 1) + { + LogLocker log_lock; + + //mb.rd_ptr () [0] = '\0'; + mb.rd_ptr (mb.rd_ptr () - result.bytes_transferred ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Server %d: handle_write_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_to_write"), + result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Server %d; %p\n"), + this->id_, + ACE_TEXT ("write")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d: wrote %B bytes ok\n"), + this->id_, + result.bytes_transferred ())); + } + + mb.release (); + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_snd_ += result.bytes_transferred (); + + if (duplex == 0) + this->initiate_read_stream (); + } + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +// ******************************************* +// Connector +// ******************************************* + +class Connector : public ACE_Asynch_Connector<Client> +{ +public: + Connector (TestData *tester); + virtual ~Connector (void); + + int start (const ACE_INET_Addr &addr, int num); + + // Virtual from ACE_Asynch_Connector + Client *make_handler (void); + +private: + TestData *tester_; +}; + +// ************************************************************* + +Connector::Connector (TestData *tester) + : tester_ (tester) +{ +} + +Connector::~Connector (void) +{ + this->cancel (); +} + +Client * +Connector::make_handler (void) +{ + return this->tester_->client_up (); +} + + +int +Connector::start (const ACE_INET_Addr& addr, int num) +{ + if (num > MAX_CLIENTS) + num = MAX_CLIENTS; + + if (num < 0) + num = 1; + + int rc = 0; + + // int open ( int pass_addresses = 0, + // ACE_Proactor *proactor = 0, + // int validate_new_connection = 0 ); + + if (this->open (1, 0, 1) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Connector::open failed"))); + return rc; + } + + for (; rc < num; rc++) + { + if (this->connect (addr) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Connector::connect failed"))); + break; + } + } + return rc; +} + + +Client::Client () +{ + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Shouldn't use this constructor!\n"))); +} + +Client::Client (TestData *tester, int id) + : tester_ (tester), + id_ (id), + handle_ (ACE_INVALID_HANDLE), + io_count_ (0), + stop_writing_ (0), + flg_cancel_ (0), + total_snd_ (0), + total_rcv_ (0), + total_w_ (0), + total_r_ (0) +{ +} + +Client::~Client (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d dtor; %d sends (%B bytes); ") + ACE_TEXT ("%d recvs (%B bytes)\n"), + this->id_, + this->total_w_, this->total_snd_, + this->total_r_, this->total_rcv_)); + if (this->io_count_ != 0) + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("(%t) Client %d deleted with %d I/O outstanding\n"), + this->id_, + this->io_count_)); + + // This test bounces data back and forth between Clients and Servers. + // Therefore, if there was significantly more data in one direction, that's + // a problem. Remember, the byte counts are unsigned values. + int issue_data_warning = 0; + if (this->total_snd_ > this->total_rcv_) + { + if (this->total_rcv_ == 0) + issue_data_warning = 1; + else if (this->total_snd_ / this->total_rcv_ > 2) + issue_data_warning = 1; + } + else + { + if (this->total_snd_ == 0) + issue_data_warning = 1; + else if (this->total_rcv_ / this->total_snd_ > 2) + issue_data_warning = 1; + } + if (issue_data_warning) + ACE_DEBUG ((LM_WARNING, + ACE_TEXT ("(%t) Above byte counts look odd; need review\n"))); + + if (this->tester_ != 0) + this->tester_->client_done (this); + + this->id_ = -1; + this->handle_= ACE_INVALID_HANDLE; + if (this->handle_ != ACE_INVALID_HANDLE) + { + ACE_OS::closesocket (this->handle_); + } + this->handle_= ACE_INVALID_HANDLE; +} + +void +Client::cancel () +{ + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + this->flg_cancel_ = 1; + this->ws_.cancel (); + this->rs_.cancel (); + return; +} + +void +Client::close () +{ + // This must be called with the lock_ held. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Closing Client %d writes; %d I/O outstanding\n"), + this->id_, this->io_count_)); + ACE_OS::shutdown (this->handle_, ACE_SHUTDOWN_WRITE); + this->stop_writing_ = 1; + return; +} + + +void +Client::addresses (const ACE_INET_Addr& /* peer */, const ACE_INET_Addr& local) +{ + ACE_TCHAR str[256]; + if (0 == local.addr_to_string (str, sizeof (str) / sizeof (ACE_TCHAR))) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d connected on %s\n"), + this->id_, + str)); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) Client %d %p\n"), + this->id_, + ACE_TEXT ("addr_to_string"))); + return; +} + + +void +Client::open (ACE_HANDLE handle, ACE_Message_Block &) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + // Don't buffer serial sends. + this->handle_ = handle; + int nodelay = 1; + ACE_SOCK_Stream option_setter (handle); + if (option_setter.set_option (ACE_IPPROTO_TCP, + TCP_NODELAY, + &nodelay, + sizeof (nodelay))) + ACE_ERROR ((LM_ERROR, "%p\n", "set_option")); + + // Open ACE_Asynch_Write_Stream + if (this->ws_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Write_Stream::open"))); + + // Open ACE_Asynch_Read_Stream + else if (this->rs_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Read_Stream::open"))); + + else if (this->initiate_write_stream () == 0) + { + if (duplex != 0) // Start an asynchronous read + this->initiate_read_stream (); + } + + if (this->io_count_ > 0) + return; + } + delete this; +} + +int +Client::initiate_write_stream (void) +{ + if (this->flg_cancel_ != 0 || + this->stop_writing_ || + this->handle_ == ACE_INVALID_HANDLE) + return -1; + + static const size_t complete_message_length = ACE_OS::strlen (complete_message); + +#if defined (ACE_WIN32) + + ACE_Message_Block *mb1 = 0, + *mb2 = 0, + *mb3 = 0; + + // No need to allocate +1 for proper printing - the memory includes it already + ACE_NEW_RETURN (mb1, + ACE_Message_Block ((char *)complete_message, + complete_message_length), + -1); + + ACE_NEW_RETURN (mb2, + ACE_Message_Block ((char *)complete_message, + complete_message_length), + -1); + + ACE_NEW_RETURN (mb3, + ACE_Message_Block ((char *)complete_message, + complete_message_length), + -1); + + mb1->wr_ptr (complete_message_length); + mb2->wr_ptr (complete_message_length); + mb3->wr_ptr (complete_message_length); + + // chain them together + mb1->cont (mb2); + mb2->cont (mb3); + + if (this->ws_.writev (*mb1, mb1->total_length ()) == -1) + { + mb1->release (); + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Stream::writev")), + -1); + } +#else /* ACE_WIN32 */ + + ACE_Message_Block *mb = 0; + + // No need to allocate +1 for proper printing - the memory includes it already + ACE_NEW_RETURN (mb, + ACE_Message_Block (complete_message, complete_message_length), + -1); + mb->wr_ptr (complete_message_length); + + if (this->ws_.write (*mb, mb->length ()) == -1) + { + mb->release (); +#if defined (ACE_WIN32) + // On peer close, WriteFile will yield ERROR_NETNAME_DELETED. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d, peer gone\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) Client %d, %p\n"), + this->id_, + ACE_TEXT ("write")), + -1); + } +#endif /* ACE_WIN32 */ + + this->io_count_++; + this->total_w_++; + return 0; +} + +int +Client::initiate_read_stream (void) +{ + if (this->flg_cancel_ != 0 || this->handle_ == ACE_INVALID_HANDLE) + return -1; + + static const size_t complete_message_length = + ACE_OS::strlen (complete_message); + +#if defined (ACE_HAS_WIN32_OVERLAPPED_IO) + ACE_Message_Block *mb1 = 0, + *mb2 = 0, + *mb3 = 0, + *mb4 = 0, + *mb5 = 0, + *mb6 = 0; + + // We allocate +1 only for proper printing - we can just set the last byte + // to '\0' before printing out + ACE_NEW_RETURN (mb1, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb2, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb3, ACE_Message_Block (complete_message_length + 1), -1); + + // Let allocate memory for one more triplet, + // This improves performance + // as we can receive more the than one block at once + // Generally, we can receive more triplets .... + ACE_NEW_RETURN (mb4, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb5, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb6, ACE_Message_Block (complete_message_length + 1), -1); + + mb1->cont (mb2); + mb2->cont (mb3); + + mb3->cont (mb4); + mb4->cont (mb5); + mb5->cont (mb6); + + + // hide last byte in each message block, reserving it for later to set '\0' + // for proper printouts + mb1->size (mb1->size () - 1); + mb2->size (mb2->size () - 1); + mb3->size (mb3->size () - 1); + + mb4->size (mb4->size () - 1); + mb5->size (mb5->size () - 1); + mb6->size (mb6->size () - 1); + + // Inititiate read + if (this->rs_.readv (*mb1, mb1->total_size () - 1) == -1) + { + mb1->release (); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Read_Stream::readv")), + -1); + } +#else /* ACE_HAS_WIN32_OVERLAPPED_IO */ + + // Try to read more chunks + size_t blksize = ( complete_message_length > BUFSIZ ) ? + complete_message_length : BUFSIZ; + + ACE_Message_Block *mb = 0; + + // We allocate +1 only for proper printing - we can just set the last byte + // to '\0' before printing out + ACE_NEW_RETURN (mb, + ACE_Message_Block (blksize + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size () - 1) == -1) + { + mb->release (); +#if defined (ACE_WIN32) + // On peer close, ReadFile will yield ERROR_NETNAME_DELETED; won't get + // a 0-byte read as we would if underlying calls used WSARecv. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d, peer closed\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Client %d, %p\n"), + this->id_, + ACE_TEXT ("read")), + -1); + } +#endif /* ACE_HAS_WIN32_OVERLAPPED_IO */ + + this->io_count_++; + this->total_r_++; + return 0; +} + +void +Client::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + ACE_Message_Block & mb = result.message_block (); + + if (loglevel > 1) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Client %d: handle_write_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_to_write"), + result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + +#if defined (ACE_WIN32) + size_t bytes_transferred = result.bytes_transferred (); + char index = 0; + for (ACE_Message_Block* mb_i = &mb; + (mb_i != 0) && (bytes_transferred > 0); + mb_i = mb_i->cont ()) + { + // write 0 at string end for proper printout (if end of mb, + // it's 0 already) + mb_i->rd_ptr()[0] = '\0'; + + size_t len = mb_i->rd_ptr () - mb_i->base (); + + // move rd_ptr backwards as required for printout + if (len >= bytes_transferred) + { + mb_i->rd_ptr (0 - bytes_transferred); + bytes_transferred = 0; + } + else + { + mb_i->rd_ptr (0 - len); + bytes_transferred -= len; + } + + ++index; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s%d = %s\n"), + ACE_TEXT ("message_block, part "), + index, + mb_i->rd_ptr ())); + } +#else /* ACE_WIN32 */ + // write 0 at string end for proper printout (if end of mb, it's 0 already) + mb.rd_ptr()[0] = '\0'; + // move rd_ptr backwards as required for printout + mb.rd_ptr (- result.bytes_transferred ()); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); +#endif /* ACE_WIN32 */ + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Client %d; %p\n"), + this->id_, + ACE_TEXT ("write")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d: wrote %B bytes ok\n"), + this->id_, + result.bytes_transferred ())); + } + + mb.release (); + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_snd_ += result.bytes_transferred (); + if (this->total_snd_ >= xfer_limit) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d sent %B, limit %B\n"), + this->id_, this->total_snd_, xfer_limit)); + this->close (); + } + if (duplex != 0) // full duplex, continue write + { + if ((this->total_snd_- this->total_rcv_) < 1024*32 ) //flow control + this->initiate_write_stream (); + } + else // half-duplex read reply, after read we will start write + this->initiate_read_stream (); + } + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +void +Client::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + ACE_Message_Block & mb = result.message_block (); + + if (loglevel > 1) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Client %d: handle_read_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_to_read"), + result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %B\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + +#if defined (ACE_WIN32) + char index = 0; + for (ACE_Message_Block* mb_i = &mb; + mb_i != 0; + mb_i = mb_i->cont ()) + { + ++index; + // write 0 at string end for proper printout + mb_i->wr_ptr()[0] = '\0'; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s%d = %s\n"), + ACE_TEXT ("message_block, part "), + index, + mb_i->rd_ptr ())); + } +#else /* ACE_WIN32 */ + // write 0 at string end for proper printout + mb.rd_ptr()[result.bytes_transferred ()] = '\0'; // for proper printout + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); +#endif /* ACE_WIN32 */ + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Client %d; %p\n"), + this->id_, + ACE_TEXT ("read")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d: read %B bytes ok\n"), + this->id_, + result.bytes_transferred ())); + } + + mb.release (); + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_rcv_ += result.bytes_transferred (); + + if (duplex != 0 || this->stop_writing_) // full duplex, continue read + this->initiate_read_stream (); + else // half-duplex write, after write we will start read + this->initiate_write_stream (); + } + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +// ************************************************************* +// Configuration helpers +// ************************************************************* +int +print_usage (int /* argc */, ACE_TCHAR *argv[]) +{ + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("\nusage: %s") + ACE_TEXT ("\n-o <max number of started aio operations for Proactor>") + ACE_TEXT ("\n-t <Proactor type> UNIX-only, Win32-default always:") + ACE_TEXT ("\n a AIOCB") + ACE_TEXT ("\n i SIG") + ACE_TEXT ("\n c CB") + ACE_TEXT ("\n s SUN") + ACE_TEXT ("\n d default") + ACE_TEXT ("\n-d <duplex mode 1-on/0-off>") + ACE_TEXT ("\n-h <host> for Client mode") + ACE_TEXT ("\n-n <number threads for Proactor pool>") + ACE_TEXT ("\n-p <port to listen/connect>") + ACE_TEXT ("\n-c <number of client instances>") + ACE_TEXT ("\n-b run client and server at the same time") + ACE_TEXT ("\n f file") + ACE_TEXT ("\n c console") + ACE_TEXT ("\n-v log level") + ACE_TEXT ("\n 0 - log errors and highlights") + ACE_TEXT ("\n 1 - log level 0 plus progress information") + ACE_TEXT ("\n 2 - log level 1 plus operation parameters and results") + ACE_TEXT ("\n-x max transfer byte count per Client") + ACE_TEXT ("\n-u show this message") + ACE_TEXT ("\n"), + argv[0] + )); + return -1; +} + +static int +set_proactor_type (const ACE_TCHAR *ptype) +{ + if (!ptype) + return 0; + + switch (ACE_OS::ace_toupper (*ptype)) + { + case 'D': + proactor_type = DEFAULT; + return 1; + case 'A': + proactor_type = AIOCB; + return 1; + case 'I': + proactor_type = SIG; + return 1; +#if defined (sun) + case 'S': + proactor_type = SUN; + return 1; +#endif /* sun */ +#if !defined (ACE_HAS_BROKEN_SIGEVENT_STRUCT) + case 'C': + proactor_type = CB; + return 1; +#endif /* !ACE_HAS_BROKEN_SIGEVENT_STRUCT */ + default: + break; + } + return 0; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + // First, set up all the defaults then let any args change them. + both = 1; // client and server simultaneosly + duplex = 1; // full duplex is on + host = ACE_LOCALHOST; // server to connect + port = ACE_DEFAULT_SERVER_PORT; // port to connect/listen + max_aio_operations = 512; // POSIX Proactor params + proactor_type = DEFAULT; // Proactor type = default + threads = 3; // size of Proactor thread pool + clients = 10; // number of clients + loglevel = 0; // log level : only errors and highlights + // Default transfer limit 50 messages per Sender + xfer_limit = 50 * ACE_OS::strlen (complete_message); + + // Linux kernels up to at least 2.6.9 (RHEL 4) can't do full duplex aio. +# if defined (linux) + duplex = 0; +#endif + + if (argc == 1) // no arguments , so one button test + return 0; + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("x:t:o:n:p:d:h:c:v:ub")); + int c; + + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'x': // xfer limit + xfer_limit = static_cast<size_t> (ACE_OS::atoi (get_opt.opt_arg ())); + if (xfer_limit == 0) + xfer_limit = 1; // Bare minimum. + break; + case 'b': // both client and server + both = 1; + break; + case 'v': // log level + loglevel = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': // duplex + duplex = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': // host for sender + host = get_opt.opt_arg (); + break; + case 'p': // port number + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': // thread pool size + threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': // number of clients + clients = ACE_OS::atoi (get_opt.opt_arg ()); + if (clients > MAX_CLIENTS) + clients = MAX_CLIENTS; + break; + case 'o': // max number of aio for proactor + max_aio_operations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': // Proactor Type + if (set_proactor_type (get_opt.opt_arg ())) + break; + return print_usage (argc, argv); + case 'u': + default: + return print_usage (argc, argv); + } // switch + } // while + + if (proactor_type == SUN && threads > 1) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Sun aiowait is not thread-safe; ") + ACE_TEXT ("changing to 1 thread\n"))); + threads = 1; + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Test")); + + if (::parse_args (argc, argv) == -1) + return -1; + + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + disable_signal (SIGPIPE, SIGPIPE); + + MyTask task1; + TestData test; + + if (task1.start (threads, proactor_type, max_aio_operations) == 0) + { + Acceptor acceptor (&test); + Connector connector (&test); + ACE_INET_Addr addr (port); + + int rc = 0; + + if (both != 0 || host == 0) // Acceptor + { + // Simplify, initial read with zero size + if (acceptor.open (addr, 0, 1) == 0) + rc = 1; + } + + if (both != 0 || host != 0) + { + if (host == 0) + host = ACE_LOCALHOST; + + if (addr.set (port, host, 1, addr.get_type ()) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), host)); + else + rc += connector.start (addr, clients); + } + + // Wait a few seconds to let things get going, then poll til + // all sessions are done. Note that when we exit this scope, the + // Acceptor and Connector will be destroyed, which should prevent + // further connections and also test how well destroyed handlers + // are handled. + ACE_OS::sleep (3); + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Sleeping til sessions run down.\n"))); + while (!test.testing_done ()) + ACE_OS::sleep (1); + + test.stop_all (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Stop Thread Pool Task\n"))); + task1.stop (); + + ACE_END_TEST; + + return 0; +} + +#else + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Test")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Threads or Asynchronous IO is unsupported.\n") + ACE_TEXT ("Proactor_Test will not be run."))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_WIN32_OVERLAPPED_IO || ACE_HAS_AIO_CALLS */ diff --git a/ACE/tests/Proactor_Test.h b/ACE/tests/Proactor_Test.h new file mode 100644 index 00000000000..f23389f73e2 --- /dev/null +++ b/ACE/tests/Proactor_Test.h @@ -0,0 +1,142 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Proactor_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// @author Alexander Libman <alibman@baltimore.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_PROACTOR_TEST_H +#define ACE_TESTS_PROACTOR_TEST_H + +#include "ace/Synch_Traits.h" +#include "ace/Thread_Mutex.h" + +// forward declaration +class TestData; + +class Server : public ACE_Service_Handler +{ +public: + Server (); + Server (TestData *tester, int id); + ~Server (void); + + int id (void) { return this->id_; } + size_t get_total_snd (void) { return this->total_snd_; } + size_t get_total_rcv (void) { return this->total_rcv_; } + long get_total_w (void) { return this->total_w_; } + long get_total_r (void) { return this->total_r_; } + + // This is called to pass the new connection's addresses. + virtual void addresses (const ACE_INET_Addr& peer, + const ACE_INET_Addr& local); + + /// This is called after the new connection has been accepted. + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + void cancel (); + +protected: + /** + * @name AIO callback handling + * + * These methods are called by the framework + */ + /// This is called when asynchronous <read> operation from the + /// socket completes. + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + + /// This is called when an asynchronous <write> to the socket + /// completes. + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + +private: + int initiate_read_stream (void); + int initiate_write_stream (ACE_Message_Block &mb, size_t nbytes); + + TestData *tester_; + int id_; + + ACE_Asynch_Read_Stream rs_; + ACE_Asynch_Write_Stream ws_; + ACE_HANDLE handle_; + ACE_SYNCH_MUTEX lock_; + + int io_count_; // Number of currently outstanding I/O requests + int flg_cancel_; + size_t total_snd_; // Number of bytes successfully sent + size_t total_rcv_; // Number of bytes successfully received + int total_w_; // Number of write operations + int total_r_; // Number of read operations +}; + +// ******************************************* +// Client +// ******************************************* + +class Client : public ACE_Service_Handler +{ +public: + + /// This is called after the new connection has been established. + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + + Client (); + Client (TestData *tester, int id); + ~Client (void); + + int id (void) { return this->id_; } + size_t get_total_snd (void) { return this->total_snd_; } + size_t get_total_rcv (void) { return this->total_rcv_; } + int get_total_w (void) { return this->total_w_; } + int get_total_r (void) { return this->total_r_; } + + // This is called to pass the new connection's addresses. + virtual void addresses (const ACE_INET_Addr& peer, + const ACE_INET_Addr& local); + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This is called when asynchronous reads from the socket complete + + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + // This is called when asynchronous writes from the socket complete + + void cancel (void); + +private: + int initiate_read_stream (void); + int initiate_write_stream (void); + void close (void); + + TestData *tester_; + int id_; + + ACE_Asynch_Read_Stream rs_; + ACE_Asynch_Write_Stream ws_; + ACE_HANDLE handle_; + + ACE_SYNCH_MUTEX lock_; + + int io_count_; + int stop_writing_; // Writes are shut down; just read. + int flg_cancel_; + size_t total_snd_; + size_t total_rcv_; + int total_w_; + int total_r_; +}; + +#endif /* ACE_TESTS_PROACTOR_TEST_H */ diff --git a/ACE/tests/Proactor_Test_IPV6.cpp b/ACE/tests/Proactor_Test_IPV6.cpp new file mode 100644 index 00000000000..f1cc290b93a --- /dev/null +++ b/ACE/tests/Proactor_Test_IPV6.cpp @@ -0,0 +1,1989 @@ +// $Id$ +// ============================================================================ +/** + * @file Proactor_Test_IPV6.cpp + * + * $Id$ + * + * This program illustrates how the ACE_Proactor can be used to + * implement an application that does various asynchronous + * operations. + * + * @author Alexander Libman <alibman@baltimore.com> + * @author Brian Buesker <bbuesker@qualcomm.com> - modified for IPv6 operation + */ +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID (tests, + Proactor_Test, + "$Id$") + +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_WIN32_OVERLAPPED_IO) || defined (ACE_HAS_AIO_CALLS)) + // This only works on Win32 platforms and on Unix platforms + // supporting POSIX aio calls. + +#include "ace/Signal.h" + +#include "ace/Service_Config.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/Object_Manager.h" +#include "ace/Get_Opt.h" + +#include "ace/Proactor.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/Asynch_Connector.h" +#include "ace/Task.h" +#include "ace/Thread_Semaphore.h" +#include "ace/OS_NS_ctype.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_socket.h" +#include "ace/os_include/netinet/os_tcp.h" + +#include "ace/Atomic_Op.h" +#include "ace/Synch_Traits.h" + +#if defined (ACE_HAS_WIN32_OVERLAPPED_IO) + +# include "ace/WIN32_Proactor.h" + +#elif defined (ACE_HAS_AIO_CALLS) + +# include "ace/POSIX_Proactor.h" +# include "ace/POSIX_CB_Proactor.h" +# include "ace/SUN_Proactor.h" + +#endif /* defined (ACE_HAS_WIN32_OVERLAPPED_IO) */ + +#include "Proactor_Test.h" + + +// Proactor Type (UNIX only, Win32 ignored) +typedef enum { DEFAULT = 0, AIOCB, SIG, SUN, CB } ProactorType; +static ProactorType proactor_type = DEFAULT; + +// POSIX : > 0 max number aio operations proactor, +static size_t max_aio_operations = 0; + +// both: 0 run client or server / depends on host +// != 0 run client and server +static int both = 0; + +// Host that we're connecting to. +static const ACE_TCHAR *host = 0; + +// number of Client instances +static int clients = 1; +const int MAX_CLIENTS = 1000; +const int MAX_SERVERS = 1000; + +// duplex mode: == 0 half-duplex +// != 0 full duplex +static int duplex = 0; + +// number threads in the Proactor thread pool +static int threads = 1; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +// Log options +static int loglevel; // 0 full , 1 only errors + +static size_t xfer_limit; // Number of bytes for Client to send. + +static char complete_message[] = + "GET / HTTP/1.1\r\n" + "Accept: */*\r\n" + "Accept-Language: C++\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "User-Agent: Proactor_Test_IPv6/1.0 (non-compatible)\r\n" + "Connection: Keep-Alive\r\n" + "\r\n"; + +class LogLocker +{ +public: + + LogLocker () { ACE_LOG_MSG->acquire (); } + virtual ~LogLocker () { ACE_LOG_MSG->release (); } +}; + + + +// Function to remove signals from the signal mask. +static int +disable_signal (int sigmin, int sigmax) +{ +#ifndef ACE_WIN32 + + sigset_t signal_set; + if (ACE_OS::sigemptyset (&signal_set) == - 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: (%P|%t):%p\n"), + ACE_TEXT ("sigemptyset failed"))); + + for (int i = sigmin; i <= sigmax; i++) + ACE_OS::sigaddset (&signal_set, i); + + // Put the <signal_set>. + if (ACE_OS::pthread_sigmask (SIG_BLOCK, &signal_set, 0) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: (%P|%t):%p\n"), + ACE_TEXT ("pthread_sigmask failed"))); +#else + ACE_UNUSED_ARG (sigmin); + ACE_UNUSED_ARG (sigmax); +#endif /* ACE_WIN32 */ + + return 1; +} + + +// ************************************************************* +// MyTask is ACE_Task resposible for : +// 1. creation and deletion of +// Proactor and Proactor thread pool +// 2. running Proactor event loop +// ************************************************************* + +/** + * @class MyTask + * + * MyTask plays role for Proactor threads pool + * + * MyTask is ACE_Task resposible for: + * 1. Creation and deletion of Proactor and Proactor thread pool + * 2. Running Proactor event loop + */ +class MyTask : public ACE_Task<ACE_MT_SYNCH> +{ +public: + MyTask (void): + lock_ (), + sem_ ((unsigned int) 0), + proactor_(0) {} + + virtual ~MyTask() + { + (void) this->stop (); + (void) this->delete_proactor(); + } + + virtual int svc (void); + + int start (int num_threads, + ProactorType type_proactor, + size_t max_op ); + int stop (void); + +private: + int create_proactor (ProactorType type_proactor, + size_t max_op); + int delete_proactor (void); + + ACE_SYNCH_RECURSIVE_MUTEX lock_; + ACE_Thread_Semaphore sem_; + ACE_Proactor * proactor_; + +}; + +int +MyTask::create_proactor (ProactorType type_proactor, size_t max_op) +{ + ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + monitor, + this->lock_, + -1); + + ACE_ASSERT (this->proactor_ == 0); + +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + + ACE_UNUSED_ARG (type_proactor); + ACE_UNUSED_ARG (max_op); + + ACE_WIN32_Proactor *proactor_impl = 0; + + ACE_NEW_RETURN (proactor_impl, + ACE_WIN32_Proactor, + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Create Proactor Type = WIN32\n"))); + +#elif defined (ACE_HAS_AIO_CALLS) + + ACE_POSIX_Proactor * proactor_impl = 0; + + switch (type_proactor) + { + case AIOCB: + ACE_NEW_RETURN (proactor_impl, + ACE_POSIX_AIOCB_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = AIOCB\n"))); + break; + +#if defined(ACE_HAS_POSIX_REALTIME_SIGNALS) + case SIG: + ACE_NEW_RETURN (proactor_impl, + ACE_POSIX_SIG_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = SIG\n"))); + break; +#endif /* ACE_HAS_POSIX_REALTIME_SIGNALS */ + +# if defined (sun) + case SUN: + ACE_NEW_RETURN (proactor_impl, + ACE_SUN_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%t) Create Proactor Type = SUN\n"))); + break; +# endif /* sun */ + +# if !defined(ACE_HAS_BROKEN_SIGEVENT_STRUCT) + case CB: + ACE_NEW_RETURN (proactor_impl, + ACE_POSIX_CB_Proactor (max_op), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = CB\n"))); + break; +# endif /* !ACE_HAS_BROKEN_SIGEVENT_STRUCT */ + + default: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Create Proactor Type = DEFAULT\n"))); + break; + } + +#endif // (ACE_WIN32) && !defined (ACE_HAS_WINCE) + + // always delete implementation 1 , not !(proactor_impl == 0) + ACE_NEW_RETURN (this->proactor_, + ACE_Proactor (proactor_impl, 1 ), + -1); + // Set new singleton and delete it in close_singleton() + ACE_Proactor::instance (this->proactor_, 1); + return 0; +} + +int +MyTask::delete_proactor (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + monitor, + this->lock_, + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Delete Proactor\n"))); + + ACE_Proactor::close_singleton (); + this->proactor_ = 0; + + return 0; +} + +int +MyTask::start (int num_threads, + ProactorType type_proactor, + size_t max_op) +{ + if (this->create_proactor (type_proactor, max_op) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to create proactor")), + -1); + + if (this->activate (THR_NEW_LWP, num_threads) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to activate thread pool")), + -1); + + for (; num_threads > 0; num_threads--) + { + sem_.acquire (); + } + + return 0; +} + + +int +MyTask::stop () +{ + if (this->proactor_ != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Calling End Proactor event loop\n"))); + + ACE_Proactor::end_event_loop (); + } + + if (this->wait () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to stop thread pool"))); + + return 0; +} + +int +MyTask::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) MyTask started\n"))); + + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + + // signal that we are ready + sem_.release (1); + + ACE_Proactor::run_event_loop (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) MyTask finished\n"))); + return 0; +} + + +// TestData collects and reports on test-related transfer and connection +// statistics. +class TestData +{ +public: + TestData (); + bool testing_done (void); + Server *server_up (void); + Client *client_up (void); + void server_done (Server *s); + void client_done (Client *c); + void stop_all (void); + void report (void); + +private: + struct Local_Stats + { + // Track number of sessions that report start, and those that report + // their end (and stats). + ACE_Atomic_Op<ACE_SYNCH_MUTEX, int> sessions_up_; + ACE_Atomic_Op<ACE_SYNCH_MUTEX, int> sessions_down_; + + // Total read and write bytes for all sessions. + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> w_cnt_; + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> r_cnt_; + // Total read and write operations issues for all sessions. + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> w_ops_; + ACE_Atomic_Op<ACE_SYNCH_MUTEX, size_t> r_ops_; + } servers_, clients_; + + ACE_SYNCH_MUTEX list_lock_; + Server *server_list_[MAX_SERVERS]; + Client *client_list_[MAX_CLIENTS]; +}; + +TestData::TestData () +{ + int i; + for (i = 0; i < MAX_SERVERS; ++i) + this->server_list_[i] = 0; + for (i = 0; i < MAX_CLIENTS; ++i) + this->client_list_[i] = 0; +} + +bool +TestData::testing_done (void) +{ + int svr_up = this->servers_.sessions_up_.value (); + int svr_dn = this->servers_.sessions_down_.value (); + int clt_up = this->clients_.sessions_up_.value (); + int clt_dn = this->clients_.sessions_down_.value (); + + if (svr_up == 0 && clt_up == 0) // No connections up yet + return false; + + return (svr_dn >= svr_up && clt_dn >= clt_up); +} + +Server * +TestData::server_up (void) +{ + ++this->servers_.sessions_up_; + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, monitor, this->list_lock_, 0); + + for (int i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] == 0) + { + ACE_NEW_RETURN (this->server_list_[i], Server (this, i), 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d up; now %d up, %d down.\n"), + i, + this->servers_.sessions_up_.value (), + this->servers_.sessions_down_.value ())); + return this->server_list_[i]; + } + } + return 0; +} + +Client * +TestData::client_up (void) +{ + ++this->clients_.sessions_up_; + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, monitor, this->list_lock_, 0); + + for (int i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] == 0) + { + ACE_NEW_RETURN (this->client_list_[i], Client (this, i), 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d up; now %d up, %d down.\n"), + i, + this->clients_.sessions_up_.value (), + this->clients_.sessions_down_.value ())); + return this->client_list_[i]; + } + } + return 0; +} + +void +TestData::server_done (Server *s) +{ + this->servers_.w_cnt_ += s->get_total_snd (); + this->servers_.r_cnt_ += s->get_total_rcv (); + this->servers_.w_ops_ += s->get_total_w (); + this->servers_.r_ops_ += s->get_total_r (); + ++this->servers_.sessions_down_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d gone; now %d up, %d down\n"), + s->id (), + this->servers_.sessions_up_.value (), + this->servers_.sessions_down_.value ())); + + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + int i; + for (i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] == s) + { + if (s->id () != i) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Server %d is pos %d in list\n"), + s->id (), + i)); + this->server_list_[i] = 0; + break; + } + } + if (i >= MAX_SERVERS) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Server %@ done but not listed\n"), s)); + + return; +} + +void +TestData::client_done (Client *c) +{ + this->clients_.w_cnt_ += c->get_total_snd (); + this->clients_.r_cnt_ += c->get_total_rcv (); + this->clients_.w_ops_ += c->get_total_w (); + this->clients_.r_ops_ += c->get_total_r (); + ++this->clients_.sessions_down_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d gone; now %d up, %d down\n"), + c->id (), + this->clients_.sessions_up_.value (), + this->clients_.sessions_down_.value ())); + + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + int i; + for (i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] == c) + { + if (c->id () != i) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Client %d is pos %d in list\n"), + c->id (), + i)); + this->client_list_[i] = 0; + break; + } + } + if (i >= MAX_CLIENTS) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Client %@ done but not listed\n"), c)); + + return; +} + +void +TestData::stop_all (void) +{ + int i; + + // Lock and cancel everything. Then release the lock, possibly allowing + // cleanups, then grab it again and delete all Servers and Clients. + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + for (i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] != 0) + this->client_list_[i]->cancel (); + } + + for (i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] != 0) + this->server_list_[i]->cancel (); + } + } + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->list_lock_); + for (i = 0; i < MAX_CLIENTS; ++i) + { + if (this->client_list_[i] != 0) + delete this->client_list_[i]; + } + + for (i = 0; i < MAX_SERVERS; ++i) + { + if (this->server_list_[i] != 0) + delete this->server_list_[i]; + } + } +} + +void +TestData::report (void) +{ + // Print statistics + ACE_TCHAR bufs [256]; + ACE_TCHAR bufr [256]; + + ACE_OS::sprintf (bufs, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->clients_.w_cnt_.value (), + this->clients_.w_ops_.value ()); + + ACE_OS::sprintf (bufr, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->clients_.r_cnt_.value (), + this->clients_.r_ops_.value ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Clients total bytes (ops): snd=%s rcv=%s\n"), + bufs, + bufr)); + + ACE_OS::sprintf (bufs, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->servers_.w_cnt_.value (), + this->servers_.w_ops_.value ()); + + ACE_OS::sprintf (bufr, + ACE_SIZE_T_FORMAT_SPECIFIER + ACE_TEXT ("(") ACE_SIZE_T_FORMAT_SPECIFIER ACE_TEXT (")"), + this->servers_.r_cnt_.value (), + this->servers_.r_ops_.value ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Servers total bytes (ops): snd=%s rcv=%s\n"), + bufs, + bufr)); + + if (this->clients_.w_cnt_.value () == 0 || + this->clients_.r_cnt_.value () == 0 || + this->servers_.w_cnt_.value () == 0 || + this->servers_.r_cnt_.value () == 0 ) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("It appears that this test didn't ") + ACE_TEXT ("really do anything. Something is very wrong.\n"))); +} + + +class Acceptor : public ACE_Asynch_Acceptor<Server> +{ +public: + Acceptor (TestData *tester); + virtual ~Acceptor (void); + + // Virtual from ACE_Asynch_Acceptor + Server *make_handler (void); + +private: + TestData *tester_; +}; + +// ************************************************************* +Acceptor::Acceptor (TestData *tester) + : tester_ (tester) +{ +} + +Acceptor::~Acceptor (void) +{ + this->cancel (); +} + +Server * +Acceptor::make_handler (void) +{ + return this->tester_->server_up (); +} + +// *************************************************** +Server::Server () +{ + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Shouldn't use this constructor!\n"))); +} + +Server::Server (TestData *tester, int id) + : tester_ (tester), + id_ (id), + handle_ (ACE_INVALID_HANDLE), + io_count_ (0), + flg_cancel_(0), + total_snd_(0), + total_rcv_(0), + total_w_ (0), + total_r_ (0) +{ +} + +Server::~Server (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d dtor; %d sends (%d bytes); ") + ACE_TEXT ("%d recvs (%d bytes)\n"), + this->id_, + this->total_w_, this->total_snd_, + this->total_r_, this->total_rcv_)); + if (this->io_count_ != 0) + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("(%t) Server %d deleted with ") + ACE_TEXT ("%d I/O outstanding\n"), + this->id_, + this->io_count_)); + + // This test bounces data back and forth between Clients and Servers. + // Therefore, if there was significantly more data in one direction, that's + // a problem. Remember, the byte counts are unsigned values. + int issue_data_warning = 0; + if (this->total_snd_ > this->total_rcv_) + { + if (this->total_rcv_ == 0) + issue_data_warning = 1; + else if (this->total_snd_ / this->total_rcv_ > 2) + issue_data_warning = 1; + } + else + { + if (this->total_snd_ == 0) + issue_data_warning = 1; + else if (this->total_rcv_ / this->total_snd_ > 2) + issue_data_warning = 1; + } + if (issue_data_warning) + ACE_DEBUG ((LM_WARNING, + ACE_TEXT ("(%t) Above byte counts look odd; need review\n"))); + + if (this->tester_ != 0) + this->tester_->server_done (this); + + if (this->handle_ != ACE_INVALID_HANDLE) + ACE_OS::closesocket (this->handle_); + + this->id_ = -1; + this->handle_= ACE_INVALID_HANDLE; +} + +void +Server::cancel () +{ + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + this->flg_cancel_ = 1; + this->ws_.cancel (); + this->rs_.cancel (); + return; +} + + +void +Server::addresses (const ACE_INET_Addr& peer, const ACE_INET_Addr&) +{ + ACE_TCHAR str[256]; + if (0 == peer.addr_to_string (str, sizeof (str) / sizeof (ACE_TCHAR))) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d connection from %s\n"), + this->id_, + str)); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) Server %d %p\n"), + this->id_, + ACE_TEXT ("addr_to_string"))); + return; +} + + +void +Server::open (ACE_HANDLE handle, ACE_Message_Block &) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + // Don't buffer serial sends. + this->handle_ = handle; + int nodelay = 1; + ACE_SOCK_Stream option_setter (handle); + if (-1 == option_setter.set_option (ACE_IPPROTO_TCP, + TCP_NODELAY, + &nodelay, + sizeof (nodelay))) + ACE_ERROR ((LM_ERROR, "%p\n", "set_option")); + + if (this->ws_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Server::ACE_Asynch_Write_Stream::open"))); + else if (this->rs_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Server::ACE_Asynch_Read_Stream::open"))); + else + this->initiate_read_stream (); + + if (this->io_count_ > 0) + return; + } + delete this; +} + +int +Server::initiate_read_stream (void) +{ + if (this->flg_cancel_ != 0 || this->handle_ == ACE_INVALID_HANDLE) + return -1; + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (1024), //BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size () - 1) == -1) + { + mb->release (); +#if defined (ACE_WIN32) + // On peer close, ReadFile will yield ERROR_NETNAME_DELETED; won't get + // a 0-byte read as we would if underlying calls used WSARecv. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d, peer closed\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Server %d, %p\n"), + this->id_, + ACE_TEXT ("read")), + -1); + } + + this->io_count_++; + this->total_r_++; + return 0; +} + +int +Server::initiate_write_stream (ACE_Message_Block &mb, size_t nbytes) +{ + if (this->flg_cancel_ != 0 || this->handle_ == ACE_INVALID_HANDLE) + { + mb.release (); + return -1; + } + + if (nbytes == 0) + { + mb.release (); + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) Server::ACE_Asynch_Write_Stream::write nbytes <0 ")), + -1); + } + + if (this->ws_.write (mb, nbytes) == -1) + { + mb.release (); +#if defined (ACE_WIN32) + // On peer close, WriteFile will yield ERROR_NETNAME_DELETED. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d, peer gone\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) Server %d, %p\n"), + this->id_, + ACE_TEXT ("write")), + -1); + } + + this->io_count_++; + this->total_w_++; + return 0; +} + +void +Server::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_ ); + + ACE_Message_Block & mb = result.message_block (); + + // Reset pointers. + mb.rd_ptr ()[result.bytes_transferred ()] = '\0'; + + if (loglevel > 1) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Server %d: handle_read_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_to_read"), + result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Server %d; %p\n"), + this->id_, + ACE_TEXT ("read")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d: read %d bytes\n"), + this->id_, + result.bytes_transferred ())); + } + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_rcv_ += result.bytes_transferred (); + + if (this->initiate_write_stream (mb, + result.bytes_transferred ()) == 0) + { + if (duplex != 0) // Initiate new read from the stream. + this->initiate_read_stream (); + } + } + else + mb.release (); + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +void +Server::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + ACE_Message_Block & mb = result.message_block (); + + if (loglevel > 1) + { + LogLocker log_lock; + + //mb.rd_ptr () [0] = '\0'; + mb.rd_ptr (mb.rd_ptr () - result.bytes_transferred ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Server %d: handle_write_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_to_write"), + result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Server %d; %p\n"), + this->id_, + ACE_TEXT ("write")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server %d: wrote %d bytes ok\n"), + this->id_, + result.bytes_transferred ())); + } + + mb.release (); + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_snd_ += result.bytes_transferred (); + + if (duplex == 0) + this->initiate_read_stream (); + } + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +// ******************************************* +// Connector +// ******************************************* + +class Connector : public ACE_Asynch_Connector<Client> +{ +public: + Connector (TestData *tester); + virtual ~Connector (void); + + int start (const ACE_INET_Addr &addr, int num); + + // Virtual from ACE_Asynch_Connector + Client *make_handler (void); + +private: + TestData *tester_; +}; + +// ************************************************************* + +Connector::Connector (TestData *tester) + : tester_ (tester) +{ +} + +Connector::~Connector (void) +{ + this->cancel (); +} + +Client * +Connector::make_handler (void) +{ + return this->tester_->client_up (); +} + + +int +Connector::start (const ACE_INET_Addr& addr, int num) +{ + if (num > MAX_CLIENTS) + num = MAX_CLIENTS; + + if (num < 0) + num = 1; + + int rc = 0; + + // int open ( int pass_addresses = 0, + // ACE_Proactor *proactor = 0, + // int validate_new_connection = 0 ); + + if (this->open (1, 0, 1) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Connector::open failed"))); + return rc; + } + + for (; rc < num; rc++) + { + ACE_INET_Addr localAddr; + if (this->connect (addr, localAddr) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Connector::connect failed for IPv6"))); + break; + } + } + return rc; +} + + +Client::Client () +{ + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Shouldn't use this constructor!\n"))); +} + +Client::Client (TestData *tester, int id) + : tester_ (tester), + id_ (id), + handle_ (ACE_INVALID_HANDLE), + io_count_ (0), + stop_writing_ (0), + flg_cancel_ (0), + total_snd_ (0), + total_rcv_ (0), + total_w_ (0), + total_r_ (0) +{ +} + +Client::~Client (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d dtor; %d sends (%d bytes); ") + ACE_TEXT ("%d recvs (%d bytes)\n"), + this->id_, + this->total_w_, this->total_snd_, + this->total_r_, this->total_rcv_)); + if (this->io_count_ != 0) + ACE_ERROR ((LM_WARNING, + ACE_TEXT ("(%t) Client %d deleted with %d I/O outstanding\n"), + this->id_, + this->io_count_)); + + // This test bounces data back and forth between Clients and Servers. + // Therefore, if there was significantly more data in one direction, that's + // a problem. Remember, the byte counts are unsigned values. + int issue_data_warning = 0; + if (this->total_snd_ > this->total_rcv_) + { + if (this->total_rcv_ == 0) + issue_data_warning = 1; + else if (this->total_snd_ / this->total_rcv_ > 2) + issue_data_warning = 1; + } + else + { + if (this->total_snd_ == 0) + issue_data_warning = 1; + else if (this->total_rcv_ / this->total_snd_ > 2) + issue_data_warning = 1; + } + if (issue_data_warning) + ACE_DEBUG ((LM_WARNING, + ACE_TEXT ("(%t) Above byte counts look odd; need review\n"))); + + if (this->tester_ != 0) + this->tester_->client_done (this); + + this->id_ = -1; + this->handle_= ACE_INVALID_HANDLE; + if (this->handle_ != ACE_INVALID_HANDLE) + { + ACE_OS::closesocket (this->handle_); + } + this->handle_= ACE_INVALID_HANDLE; +} + +void +Client::cancel () +{ + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + this->flg_cancel_ = 1; + this->ws_.cancel (); + this->rs_.cancel (); + return; +} + +void +Client::close () +{ + // This must be called with the lock_ held. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Closing Client %d writes; %d I/O outstanding\n"), + this->id_, this->io_count_)); + ACE_OS::shutdown (this->handle_, ACE_SHUTDOWN_WRITE); + this->stop_writing_ = 1; + return; +} + + +void +Client::addresses (const ACE_INET_Addr& peer, const ACE_INET_Addr& local) +{ + char my_name[256]; + char peer_name[256]; + ACE_TCHAR local_str[256]; + ACE_INET_Addr addr ((u_short) 0, host); + + // This checks to make sure the peer address given to us matches what + // we expect it to be. + if (0 != peer.get_host_addr (peer_name, sizeof (peer_name))) + { + if (0 != addr.get_host_addr (my_name, sizeof (my_name))) + { + if (0 != ACE_OS::strncmp (peer_name, my_name, sizeof (my_name))) + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("(%t) Sender %d peer address (%C) does not ") + ACE_TEXT ("match host address (%C)\n"), + this->id_, + peer_name, my_name)); + return; + } + } + else + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("(%t) Sender %d unable to convert host addr\n"), + this->id_)); + return; + } + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Sender %d unable to convert peer addr\n"), + this->id_)); + return; + } + + if (0 == local.addr_to_string (local_str, + sizeof (local_str) / sizeof (ACE_TCHAR))) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d connected on %s\n"), + this->id_, + local_str)); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) Client %d %p\n"), + this->id_, + ACE_TEXT ("addr_to_string"))); + return; +} + + +void +Client::open (ACE_HANDLE handle, ACE_Message_Block &) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + // Don't buffer serial sends. + this->handle_ = handle; + int nodelay = 1; + ACE_SOCK_Stream option_setter (handle); + if (option_setter.set_option (ACE_IPPROTO_TCP, + TCP_NODELAY, + &nodelay, + sizeof (nodelay))) + ACE_ERROR ((LM_ERROR, "%p\n", "set_option")); + + // Open ACE_Asynch_Write_Stream + if (this->ws_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Write_Stream::open"))); + + // Open ACE_Asynch_Read_Stream + else if (this->rs_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Read_Stream::open"))); + + else if (this->initiate_write_stream () == 0) + { + if (duplex != 0) // Start an asynchronous read + this->initiate_read_stream (); + } + + if (this->io_count_ > 0) + return; + } + delete this; +} + +int +Client::initiate_write_stream (void) +{ + if (this->flg_cancel_ != 0 || + this->stop_writing_ || + this->handle_ == ACE_INVALID_HANDLE) + return -1; + + static const size_t complete_message_length = ACE_OS::strlen (complete_message); + +#if (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) + + ACE_Message_Block *mb1 = 0, + *mb2 = 0, + *mb3 = 0; + + // No need to allocate +1 for proper printing - the memory includes it already + ACE_NEW_RETURN (mb1, + ACE_Message_Block ((char *)complete_message, + complete_message_length), + -1); + + ACE_NEW_RETURN (mb2, + ACE_Message_Block ((char *)complete_message, + complete_message_length), + -1); + + ACE_NEW_RETURN (mb3, + ACE_Message_Block ((char *)complete_message, + complete_message_length), + -1); + + mb1->wr_ptr (complete_message_length); + mb2->wr_ptr (complete_message_length); + mb3->wr_ptr (complete_message_length); + + // chain them together + mb1->cont (mb2); + mb2->cont (mb3); + + if (this->ws_.writev (*mb1, mb1->total_length ()) == -1) + { + mb1->release (); + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Stream::writev")), + -1); + } +#else /* (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) */ + + ACE_Message_Block *mb = 0; + + // No need to allocate +1 for proper printing - the memory includes it already + ACE_NEW_RETURN (mb, + ACE_Message_Block (complete_message, complete_message_length), + -1); + mb->wr_ptr (complete_message_length); + + if (this->ws_.write (*mb, mb->length ()) == -1) + { + mb->release (); +#if defined (ACE_WIN32) + // On peer close, WriteFile will yield ERROR_NETNAME_DELETED. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d, peer gone\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("(%t) Client %d, %p\n"), + this->id_, + ACE_TEXT ("write")), + -1); + } +#endif /* (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) */ + + this->io_count_++; + this->total_w_++; + return 0; +} + +int +Client::initiate_read_stream (void) +{ + if (this->flg_cancel_ != 0 || this->handle_ == ACE_INVALID_HANDLE) + return -1; + + static const size_t complete_message_length = + ACE_OS::strlen (complete_message); + +#if (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) + ACE_Message_Block *mb1 = 0, + *mb2 = 0, + *mb3 = 0, + *mb4 = 0, + *mb5 = 0, + *mb6 = 0; + + // We allocate +1 only for proper printing - we can just set the last byte + // to '\0' before printing out + ACE_NEW_RETURN (mb1, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb2, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb3, ACE_Message_Block (complete_message_length + 1), -1); + + // Let allocate memory for one more triplet, + // This improves performance + // as we can receive more the than one block at once + // Generally, we can receive more triplets .... + ACE_NEW_RETURN (mb4, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb5, ACE_Message_Block (complete_message_length + 1), -1); + ACE_NEW_RETURN (mb6, ACE_Message_Block (complete_message_length + 1), -1); + + mb1->cont (mb2); + mb2->cont (mb3); + + mb3->cont (mb4); + mb4->cont (mb5); + mb5->cont (mb6); + + + // hide last byte in each message block, reserving it for later to set '\0' + // for proper printouts + mb1->size (mb1->size () - 1); + mb2->size (mb2->size () - 1); + mb3->size (mb3->size () - 1); + + mb4->size (mb4->size () - 1); + mb5->size (mb5->size () - 1); + mb6->size (mb6->size () - 1); + + // Inititiate read + if (this->rs_.readv (*mb1, mb1->total_size () - 1) == -1) + { + mb1->release (); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Client::ACE_Asynch_Read_Stream::readv")), + -1); + } +#else /* (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) */ + + // Try to read more chunks + size_t blksize = ( complete_message_length > BUFSIZ ) ? + complete_message_length : BUFSIZ; + + ACE_Message_Block *mb = 0; + + // We allocate +1 only for proper printing - we can just set the last byte + // to '\0' before printing out + ACE_NEW_RETURN (mb, + ACE_Message_Block (blksize + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size () - 1) == -1) + { + mb->release (); +#if defined (ACE_WIN32) + // On peer close, ReadFile will yield ERROR_NETNAME_DELETED; won't get + // a 0-byte read as we would if underlying calls used WSARecv. + if (ACE_OS::last_error () == ERROR_NETNAME_DELETED) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d, peer closed\n"), + this->id_), + -1); +#endif /* ACE_WIN32 */ + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Client %d, %p\n"), + this->id_, + ACE_TEXT ("read")), + -1); + } +#endif /* (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) */ + + this->io_count_++; + this->total_r_++; + return 0; +} + +void +Client::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + ACE_Message_Block & mb = result.message_block (); + + if (loglevel > 1) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Client %d: handle_write_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_to_write"), + result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + +#if (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) + size_t bytes_transferred = result.bytes_transferred (); + char index = 0; + for (ACE_Message_Block* mb_i = &mb; + (mb_i != 0) && (bytes_transferred > 0); + mb_i = mb_i->cont ()) + { + // write 0 at string end for proper printout (if end of mb, it's 0 already) + mb_i->rd_ptr()[0] = '\0'; + + size_t len = mb_i->rd_ptr () - mb_i->base (); + + // move rd_ptr backwards as required for printout + if (len >= bytes_transferred) + { + mb_i->rd_ptr (0 - bytes_transferred); + bytes_transferred = 0; + } + else + { + mb_i->rd_ptr (0 - len); + bytes_transferred -= len; + } + + ++index; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s%d = %s\n"), + ACE_TEXT ("message_block, part "), + index, + mb_i->rd_ptr ())); + } +#else /* (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) */ + // write 0 at string end for proper printout (if end of mb, it's 0 already) + mb.rd_ptr()[0] = '\0'; + // move rd_ptr backwards as required for printout + mb.rd_ptr (- result.bytes_transferred ()); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); +#endif /* (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) */ + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Client %d; %p\n"), + this->id_, + ACE_TEXT ("write")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d: wrote %d bytes ok\n"), + this->id_, + result.bytes_transferred ())); + } + + mb.release (); + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_snd_ += result.bytes_transferred (); + if (this->total_snd_ >= xfer_limit) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d sent %d, limit %d\n"), + this->id_, this->total_snd_, xfer_limit)); + this->close (); + } + if (duplex != 0) // full duplex, continue write + { + if ((this->total_snd_- this->total_rcv_) < 1024*32 ) //flow control + this->initiate_write_stream (); + } + else // half-duplex read reply, after read we will start write + this->initiate_read_stream (); + } + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +void +Client::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + { + ACE_GUARD (ACE_SYNCH_MUTEX, monitor, this->lock_); + + ACE_Message_Block & mb = result.message_block (); + + if (loglevel > 1) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** Client %d: handle_read_stream() ****\n"), + this->id_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_to_read"), + result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("handle"), + result.handle ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("bytes_transfered"), + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("act"), + result.act ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("success"), + result.success ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %@\n"), + ACE_TEXT ("completion_key"), + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %d\n"), + ACE_TEXT ("error"), + result.error ())); + +#if defined (ACE_WIN32) + char index = 0; + for (ACE_Message_Block* mb_i = &mb; + mb_i != 0; + mb_i = mb_i->cont ()) + { + ++index; + // write 0 at string end for proper printout + mb_i->wr_ptr()[0] = '\0'; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s%d = %s\n"), + ACE_TEXT ("message_block, part "), + index, + mb_i->rd_ptr ())); + } +#else /* ACE_WIN32 */ + // write 0 at string end for proper printout + mb.rd_ptr()[result.bytes_transferred ()] = '\0'; // for proper printout + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s = %s\n"), + ACE_TEXT ("message_block"), + mb.rd_ptr ())); +#endif /* ACE_WIN32 */ + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** end of message ****************\n"))); + } + else if (result.error () != 0) + { + ACE_Log_Priority prio; +#if defined (ACE_WIN32) + if (result.error () == ERROR_OPERATION_ABORTED) + prio = LM_DEBUG; +#else + if (result.error () == ECANCELED) + prio = LM_DEBUG; +#endif /* ACE_WIN32 */ + else + prio = LM_ERROR; + ACE_Log_Msg::instance ()->errnum (result.error ()); + ACE_Log_Msg::instance ()->log (prio, + ACE_TEXT ("(%t) Client %d; %p\n"), + this->id_, + ACE_TEXT ("read")); + } + else if (loglevel > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client %d: read %d bytes ok\n"), + this->id_, + result.bytes_transferred ())); + } + + mb.release (); + + if (result.error () == 0 && result.bytes_transferred () > 0) + { + this->total_rcv_ += result.bytes_transferred (); + + if (duplex != 0 || this->stop_writing_) // full duplex, continue read + this->initiate_read_stream (); + else // half-duplex write, after write we will start read + this->initiate_write_stream (); + } + + this->io_count_--; + if (this->io_count_ > 0) + return; + } + delete this; +} + +// ************************************************************* +// Configuration helpers +// ************************************************************* +int +print_usage (int /* argc */, ACE_TCHAR *argv[]) +{ + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("\nusage: %s") + ACE_TEXT ("\n-o <max number of started aio operations for Proactor>") + ACE_TEXT ("\n-t <Proactor type> UNIX-only, Win32-default always:") + ACE_TEXT ("\n a AIOCB") + ACE_TEXT ("\n i SIG") + ACE_TEXT ("\n c CB") + ACE_TEXT ("\n s SUN") + ACE_TEXT ("\n d default") + ACE_TEXT ("\n-d <duplex mode 1-on/0-off>") + ACE_TEXT ("\n-h <host> for Client mode") + ACE_TEXT ("\n-n <number threads for Proactor pool>") + ACE_TEXT ("\n-p <port to listen/connect>") + ACE_TEXT ("\n-c <number of client instances>") + ACE_TEXT ("\n-b run client and server at the same time") + ACE_TEXT ("\n f file") + ACE_TEXT ("\n c console") + ACE_TEXT ("\n-v log level") + ACE_TEXT ("\n 0 - log errors and highlights") + ACE_TEXT ("\n 1 - log level 0 plus progress information") + ACE_TEXT ("\n 2 - log level 1 plus operation parameters and results") + ACE_TEXT ("\n-x max transfer byte count per Client") + ACE_TEXT ("\n-u show this message") + ACE_TEXT ("\n"), + argv[0] + )); + return -1; +} + +static int +set_proactor_type (const ACE_TCHAR *ptype) +{ + if (!ptype) + return 0; + + switch (ACE_OS::ace_toupper (*ptype)) + { + case 'D': + proactor_type = DEFAULT; + return 1; + case 'A': + proactor_type = AIOCB; + return 1; + case 'I': + proactor_type = SIG; + return 1; +#if defined (sun) + case 'S': + proactor_type = SUN; + return 1; +#endif /* sun */ +#if !defined (ACE_HAS_BROKEN_SIGEVENT_STRUCT) + case 'C': + proactor_type = CB; + return 1; +#endif /* !ACE_HAS_BROKEN_SIGEVENT_STRUCT */ + default: + break; + } + return 0; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + // First, set up all the defaults then let any args change them. + both = 1; // client and server simultaneosly + duplex = 1; // full duplex is on +#if defined (ACE_HAS_IPV6) + host = ACE_IPV6_LOCALHOST; // server to connect (IPv6 localhost) +#else + host = ACE_LOCALHOST; +#endif /*ACE_HAS_IPV6*/ + port = ACE_DEFAULT_SERVER_PORT; // port to connect/listen + max_aio_operations = 512; // POSIX Proactor params + proactor_type = DEFAULT; // Proactor type = default + threads = 3; // size of Proactor thread pool + clients = 10; // number of clients + loglevel = 0; // log level : only errors and highlights + // Default transfer limit 50 messages per Sender + xfer_limit = 50 * ACE_OS::strlen (complete_message); + + // Linux kernels up to at least 2.6.9 (RHEL 4) can't do full duplex aio. +# if defined (linux) + duplex = 0; +#endif + + if (argc == 1) // no arguments , so one button test + return 0; + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("x:t:o:n:p:d:h:c:v:ub")); + int c; + + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'x': // xfer limit + xfer_limit = static_cast<size_t> (ACE_OS::atoi (get_opt.opt_arg ())); + if (xfer_limit == 0) + xfer_limit = 1; // Bare minimum. + break; + case 'b': // both client and server + both = 1; + break; + case 'v': // log level + loglevel = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': // duplex + duplex = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': // host for sender + host = get_opt.opt_arg (); + break; + case 'p': // port number + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': // thread pool size + threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': // number of clients + clients = ACE_OS::atoi (get_opt.opt_arg ()); + if (clients > MAX_CLIENTS) + clients = MAX_CLIENTS; + break; + case 'o': // max number of aio for proactor + max_aio_operations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': // Proactor Type + if (set_proactor_type (get_opt.opt_arg ())) + break; + return print_usage (argc, argv); + case 'u': + default: + return print_usage (argc, argv); + } // switch + } // while + + if (proactor_type == SUN && threads > 1) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Sun aiowait is not thread-safe; ") + ACE_TEXT ("changing to 1 thread\n"))); + threads = 1; + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Test_IPV6")); + + if (::parse_args (argc, argv) == -1) + return -1; + +#if defined (ACE_HAS_IPV6) + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + disable_signal (SIGPIPE, SIGPIPE); + + MyTask task1; + TestData test; + + if (task1.start (threads, proactor_type, max_aio_operations) == 0) + { + Acceptor acceptor (&test); + Connector connector (&test); + ACE_INET_Addr addr (port, "::"); + + int rc = 0; + + if (both != 0 || host == 0) // Acceptor + { + // Simplify, initial read with zero size + if (acceptor.open (addr, 0, 1) == 0) + rc = 1; + } + + if (both != 0 || host != 0) + { + if (host == 0) + host = ACE_IPV6_LOCALHOST; + + if (addr.set (port, host, 1, addr.get_type ()) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), host)); + else + rc += connector.start (addr, clients); + } + + // Wait a few seconds to let things get going, then poll til + // all sessions are done. Note that when we exit this scope, the + // Acceptor and Connector will be destroyed, which should prevent + // further connections and also test how well destroyed handlers + // are handled. + ACE_OS::sleep (3); + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Sleeping til sessions run down.\n"))); + while (!test.testing_done ()) + ACE_OS::sleep (1); + + test.stop_all (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Stop Thread Pool Task\n"))); + task1.stop (); + +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + + return 0; +} + +#else + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Test_IPV6")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Threads or Asynchronous IO is unsupported.\n") + ACE_TEXT ("Proactor_Test will not be run."))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_WIN32_OVERLAPPED_IO || ACE_HAS_AIO_CALLS */ diff --git a/ACE/tests/Proactor_Timer_Test.cpp b/ACE/tests/Proactor_Timer_Test.cpp new file mode 100644 index 00000000000..7d81bcb5ef1 --- /dev/null +++ b/ACE/tests/Proactor_Timer_Test.cpp @@ -0,0 +1,343 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Proctor_Timer_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the timer mechanism of +// the <ACE_Proactor>. Scheduling timers, handling expired timers and +// cancelling scheduled timers are all exercised in this test. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu>, +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, and +// Miljenko Norsic <Miljenko.Norsic@etk.ericsson.se> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Trace.h" + +ACE_RCSID (tests, + Proactor_Timer_Test, + "$Id$") + +#if defined (ACE_HAS_WIN32_OVERLAPPED_IO) || defined (ACE_HAS_AIO_CALLS) + // This only works on Win32 platforms and on Unix platforms + // supporting POSIX aio calls. + +#include "ace/OS_NS_unistd.h" +#include "ace/Timer_Queue.h" +#include "ace/Proactor.h" +#include "ace/High_Res_Timer.h" +#include "ace/Asynch_IO.h" + +static int done = 0; +static size_t counter = 0; +static int odd = 0; + +class Time_Handler : public ACE_Handler +{ +public: + Time_Handler (); + // Default constructor + + virtual void handle_time_out (const ACE_Time_Value &tv, const void *arg); + // Handle the timeout. + + long timer_id (void) const; + // Return our timer id. + + void timer_id (long); + // Set our timer id; + +private: + long timer_id_; + // Stores the id of this timer. +}; + +/* + * Need a variant of this that will track if a repeating timer is working + * correctly. This class should be scheduled with a repeating timer that + * repeats on a specified number of seconds. This class will let two + * expirations happen then wait in handle_time_out() longer than the repeat + * time to cause at least one timer expiration to be queued up while we're + * waiting; then cancel the timer. + */ +class Repeat_Timer_Handler : public ACE_Handler +{ +public: + static const int REPEAT_INTERVAL = 2; + + // Constructor arg tells how many seconds we intend to do the repeat with. + // The internals will use this to tell how long to wait in order to cause + // a timer expiration to be missed and queued up. + Repeat_Timer_Handler (const int repeat_time = REPEAT_INTERVAL) + : repeat_secs_ (repeat_time), expirations_ (0) {}; + + ~Repeat_Timer_Handler (); + + // Handle the timeout. + virtual void handle_time_out (const ACE_Time_Value &tv, const void *arg); + +private: + int repeat_secs_; + int expirations_; +}; + + +Time_Handler::Time_Handler (void) + : timer_id_ (-1) +{ + // Nothing +} + +void +Time_Handler::handle_time_out (const ACE_Time_Value &, const void *arg) +{ + size_t current_counter = *(reinterpret_cast<const size_t *> (arg)); + if (current_counter != counter) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected timer %d, not %d\n"), + counter, + current_counter)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("[%@] Timer id %d with counter #%d|%d expired.\n"), + this, + this->timer_id (), + counter, + current_counter)); + + if (current_counter == (ACE_MAX_TIMERS - 1)) + done = 1; + else if (counter == ACE_MAX_TIMERS - 1) + { + done = 1; + return; + } + + counter += (1 + odd); + return; +} + +long +Time_Handler::timer_id (void) const +{ + return this->timer_id_; +} + +void +Time_Handler::timer_id (long t) +{ + this->timer_id_ = t; +} + +Repeat_Timer_Handler::~Repeat_Timer_Handler () +{ + if (this->expirations_ == 2) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Repeater expired twice; correct\n"))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Repeater expired %d times; should be 2\n"), + this->expirations_)); +} + +void +Repeat_Timer_Handler::handle_time_out (const ACE_Time_Value &, const void *) +{ + // Let the first one go. + if (++this->expirations_ == 1) + return; + + if (this->expirations_ == 2) + { + ACE_OS::sleep (this->repeat_secs_ + 1); + int canceled = this->proactor ()->cancel_timer (*this); + if (canceled != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Repeater cancel timer: %d; should be 1\n"), + canceled)); + } + delete this; + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Repeater expiration #%d; should get only 2\n"), + this->expirations_)); + } + return; +} + +static void +test_registering_all_handlers (void) +{ + ACE_Trace t (ACE_TEXT ("test_registering_all_handler"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt[ACE_MAX_TIMERS]; + long t_id[ACE_MAX_TIMERS]; + size_t which[ACE_MAX_TIMERS]; + long secs = 0; + size_t i = 0; + for ( ; i < ACE_MAX_TIMERS; i++, secs++) + { + which[i] = i; + t_id[i] = + ACE_Proactor::instance ()->schedule_timer + (rt[i], &which[i], ACE_Time_Value (2 * secs + 1)); + if (t_id[i] == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("schedule_timer"))); + rt[i].timer_id (t_id[i]); + } + + while (!done) + ACE_Proactor::instance ()->handle_events (); +} + +static void +test_registering_one_handler (void) +{ + ACE_Trace t (ACE_TEXT ("test_registering_one_handler"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt[ACE_MAX_TIMERS]; + long t_id[ACE_MAX_TIMERS]; + size_t which[ACE_MAX_TIMERS]; + + done = 0; + counter = 0; + long secs = 0; + size_t i = 0; + for ( ; i < ACE_MAX_TIMERS; i++, secs++) + { + which[i] = i; + t_id[i] = + ACE_Proactor::instance ()->schedule_timer + (rt[0], &which[i], ACE_Time_Value (2 * secs + 1)); + if (t_id[i] == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("schedule_timer"))); + } + + while (!done) + ACE_Proactor::instance ()->handle_events (); +} + +static void +test_canceling_odd_timers (void) +{ + ACE_Trace t (ACE_TEXT ("test_canceling_odd_timers"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt[ACE_MAX_TIMERS]; + long t_id[ACE_MAX_TIMERS]; + size_t which[ACE_MAX_TIMERS]; + + done = 0; + counter = 1; + odd = 1; + size_t i = 0; + long secs = 0; + for ( ; i < ACE_MAX_TIMERS; i++, secs++) + { + which[i] = i; + t_id[i] = ACE_Proactor::instance ()->schedule_timer + (rt[i], &which[i], ACE_Time_Value (2 * secs + 1)); + if (t_id[i] == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("schedule_timer"))); + rt[i].timer_id (t_id[i]); + } + + for (i = 0; i < ACE_MAX_TIMERS; i++) + // Cancel handlers with odd numbered timer ids. + if (ACE_ODD (rt[i].timer_id ())) + { + if (ACE_Proactor::instance ()->cancel_timer (rt[i].timer_id ()) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("cancel_timer"))); + } + + while (!done) + ACE_Proactor::instance ()->handle_events (); +} + +static void +test_cancel_repeat_timer (void) +{ + Repeat_Timer_Handler *handler = new Repeat_Timer_Handler; + ACE_Time_Value timeout (Repeat_Timer_Handler::REPEAT_INTERVAL); + long t_id = ACE_Proactor::instance ()->schedule_repeating_timer + (*handler, 0, timeout); + if (t_id == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("schedule_repeating_timer"))); + delete handler; + return; + } + + ACE_Time_Value test_timer (4 * Repeat_Timer_Handler::REPEAT_INTERVAL); + if (-1 == ACE_Proactor::instance ()->proactor_run_event_loop (test_timer)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("proactor loop fail"))); + + // handler should be deleted by its own handle_time_out(). + return; +} + + +// If any command line arg is given, run the test with high res timer +// queue. Else run it normally. +int +run_main (int argc, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Timer_Test")); + + if (argc > 1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Running with high-res timer queue\n"))); + ACE_Proactor *r = ACE_Proactor::instance (); + (void) ACE_High_Res_Timer::global_scale_factor (); + r->timer_queue ()->gettimeofday (&ACE_High_Res_Timer::gettimeofday_hr); + } + + // Register all different handlers, i.e., one per timer. + test_registering_all_handlers (); + + // Now try multiple timers for ONE event handler (should produce the + // same result). + test_registering_one_handler (); + + // Try canceling handlers with odd numbered timer ids. + test_canceling_odd_timers (); + + test_cancel_repeat_timer (); + + ACE_END_TEST; + return 0; +} + +#else + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Proactor_Timer_Test")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Asynchronous IO is unsupported.\n") + ACE_TEXT ("Proactor_Timer_Test will not be run."))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_WIN32_OVERLAPPED_IO || ACE_HAS_AIO_CALLS */ diff --git a/ACE/tests/Process_Manager_Test.cpp b/ACE/tests/Process_Manager_Test.cpp new file mode 100644 index 00000000000..e433098c906 --- /dev/null +++ b/ACE/tests/Process_Manager_Test.cpp @@ -0,0 +1,479 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Process_Manager_Test.cpp +// +// = DESCRIPTION +// This program tests the various methods provided by +// <ACE_Process_Manager>. It illustrates both the explicit <wait> +// functions and the Reactor-style auto-reaping. There's an +// Exit_Handler class that can print out (in Debug mode) when a +// child has been reaped. +// +// The child processes spawned are simply this program itself, with +// an integer argument specifying how long to "process" (actually, +// the child just sleeps for the specified length of time). +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> and +// Dave Madden <dhm@mersenne.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/SString.h" +#include "ace/Atomic_Op.h" +#include "ace/Task.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_string.h" +#include "ace/Process_Manager.h" +#include "ace/Synch_Traits.h" +#include "ace/Get_Opt.h" +#include "ace/Thread.h" +#include "ace/Reactor.h" + +ACE_RCSID(tests, Process_Manager_Test, "Process_Manager_Test.cpp,v 4.11 1999/09/02 04:36:30 schmidt Exp") + +static u_int debug_test = 0; + +class Exit_Handler : public ACE_Event_Handler +{ +public: + Exit_Handler (const char *msg): msg_ (msg) { } + + virtual ~Exit_Handler (void) { } + + virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask) + { + delete this; + return 0; + } + + virtual int handle_exit (ACE_Process *proc) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Exit_Handler(%s) got %d: %d\n"), + msg_, + int (proc->getpid ()), + int (proc->exit_code ()) )); + return 0; + } +private: + const char *msg_; +}; + +static void +usage (const ACE_TCHAR *argv0) +{ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s [-d] [sleep-seconds]\n"), + argv0)); + ACE_OS::exit (0); +} + +static pid_t +spawn_child (const ACE_TCHAR *argv0, + ACE_Process_Manager &mgr, + int sleep_time = 0) +{ +#if defined (ACE_WIN32) +const ACE_TCHAR *cmdline_format = ACE_TEXT("\"%s\" %s %d"); +#elif !defined (ACE_USES_WCHAR) +const ACE_TCHAR *cmdline_format = ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR ACE_TEXT("%s %s %d"); +#else +const ACE_TCHAR *cmdline_format = ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR ACE_TEXT("%ls %ls %d"); +#endif + ACE_Process_Options opts; + + opts.command_line (cmdline_format, + argv0, + debug_test ? ACE_TEXT ("-d") : ACE_TEXT (""), + sleep_time); + + pid_t result = mgr.spawn (opts); + + if (result != ACE_INVALID_PID) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) spawned child: pid %d time %d\n"), + int (result), sleep_time)); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn failed"))); + + return result; +} + +ACE_CString order; + +ACE_Atomic_Op<ACE_SYNCH_MUTEX, int> running_tasks = 0; + +class Process_Task : public ACE_Task<ACE_SYNCH> +{ +public: + Process_Task (const ACE_TCHAR *argv0, + ACE_Process_Manager &mgr, + int sleep_time) + : argv0_ (argv0), + mgr_ (mgr), + sleep_time_ (sleep_time) { } + + int open (void*) + { + char tmp[10]; + order += ACE_OS::itoa (sleep_time_, tmp, 10); + running_tasks++; + activate (); + return 0; + } + + int svc (void) + { + int result = 0; + ACE_exitcode exitcode; + pid_t my_child = spawn_child (argv0_, + mgr_, + sleep_time_); + result = mgr_.wait (my_child, + &exitcode); + if (result != my_child) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Error: expected to reap child (%d); got %d\n"), + my_child, + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + //test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) reaped child, pid %d: %d\n"), + my_child, + exitcode)); + char tmp[10]; + order += ACE_OS::itoa (sleep_time_, tmp, 10); + return 0; + } + + int close (u_long) + { + running_tasks--; + return 0; + } + +private: + const ACE_TCHAR *argv0_; + ACE_Process_Manager &mgr_; + int sleep_time_; +}; + +static int +command_line_test (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing for last character of command line\n"))); + int result = 0; + const ACE_TCHAR *command = ACE_TEXT ("test Hello"); + size_t command_len = ACE_OS::strlen (command); + ACE_Process_Options options (1, command_len + 1); + options.command_line (command); + ACE_TCHAR * const *procargv = options.command_line_argv (); + if (ACE_OS::strcmp (procargv [1], ACE_TEXT ("Hello")) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("command_line_test failed: expected \"%s\"; got \"%s\"\n"), + ACE_TEXT ("Hello"), + procargv [1])); + result = 1; + } + return result; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt args (argc, argv, ACE_TEXT ("d")); + + for (int arg = args (); arg != EOF; arg = args ()) + switch (arg) + { + case 'd': + debug_test = 1u; + break; + default: + usage (argv[0]); + break; + } + + if (args.opt_ind () == argc - 1) + { + // child process: sleep & exit + ACE_TCHAR lognm[MAXPATHLEN]; + int mypid (ACE_OS::getpid ()); + ACE_OS::sprintf(lognm, ACE_TEXT ("Process_Manager_Test-child-%d"), mypid); + + ACE_START_TEST (lognm); + int secs = ACE_OS::atoi (argv[args.opt_ind ()]); + ACE_OS::sleep (secs ? secs : 1); + if (debug_test) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%T: pid %P about to exit with code %d\n"), + secs)); + ACE_END_LOG; + + return secs; + } + + if (args.opt_ind () != argc) // incorrect usage + usage (argv[0]); + + ACE_START_TEST (ACE_TEXT ("Process_Manager_Test")); + + int result = 0; + int test_status = 0; + + if ((result = command_line_test ()) != 0) + test_status = result; + + // Try the explicit <ACE_Process_Manager::wait> functions + + ACE_Process_Manager mgr; + + mgr.register_handler (new Exit_Handler ("default")); + + ACE_exitcode exitcode; + + // -------------------------------------------------- + // wait for a specific PID + pid_t child1 = spawn_child (argv[0], + mgr, + 1); + result = mgr.wait (child1, + &exitcode); + + if (result != child1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Error: expected to reap child1 (%d); got %d\n"), + child1, + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) reaped child1, pid %d: %d\n"), + child1, + exitcode)); + + // -------------------------------------------------- + // wait for a specific PID; another should finish first + pid_t child2 = spawn_child (argv[0], + mgr, + 1); + pid_t child3 = spawn_child (argv[0], + mgr, + 4); + result = mgr.wait (child3, + &exitcode); + + if (result != child3) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Error: expected to reap child3 (%d); got %d\n"), + child3, + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) reaped child 3, pid %d: %d\n"), + child3, + exitcode)); + + // Now wait for any...should get the one that finished earlier. + + result = mgr.wait (0, &exitcode); + + if (result != child2) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Error: expected to reap child2 (%d); got %d\n"), + child2, + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) reaped child 2, pid %d: %d\n"), + result, + exitcode)); + + // -------------------------------------------------- + // Try the timed wait functions + + // This one shouldn't timeout: + pid_t child4 = spawn_child (argv[0], + mgr, + 1); + result = mgr.wait (0, ACE_Time_Value (4), &exitcode); + + if (result != child4) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Error: expected to reap child4 (%d); got %d\n"), + child4, + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) reaped child 4 pid %d: %d\n"), + result, + exitcode)); + + // This one should timeout: + pid_t child5 = spawn_child (argv[0], + mgr, + 4); + result = mgr.wait (0, ACE_Time_Value (1), &exitcode); + if (result != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Error: expected wait to time out; got %d\n"), + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Correctly timed out wait at child 5\n"))); + + // Now wait indefinitely to clean up... + result = mgr.wait (0, &exitcode); + + if (result != child5) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: expected to reap child5 pid %d; got %d\n"), + child5, + result)); + if (result == ACE_INVALID_PID) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error"))); + test_status = 1; + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) reaped child 5, pid %d: %d\n"), + result, + exitcode)); + + // Terminate a child process and make sure we can wait for it. + pid_t child6 = spawn_child (argv[0], mgr, 5); + ACE_exitcode status6; + if (-1 == mgr.terminate (child6)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("terminate child6"))); + test_status = 1; + mgr.wait (child6, &status6); // Wait for child to exit just to clean up + } + else + { + if (-1 == mgr.wait (child6, &status6)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) wait on child6 reported ACE_INVALID_PID\n"))); + test_status = 1; + } + else + { + // Get the results of the termination. +#if !defined(ACE_WIN32) + if (WIFSIGNALED (status6) != 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) child6 died on signal %d - correct\n"), + WTERMSIG (status6))); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) child6 should have died on signal, ") + ACE_TEXT ("but didn't; exit status %d\n"), + WEXITSTATUS (status6))); +#else + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("(%P) The process terminated with exit code %d\n"), + status6)); +#endif /*ACE_WIN32*/ + } + } + + Process_Task task1 (argv[0], mgr, 3); + Process_Task task2 (argv[0], mgr, 2); + Process_Task task3 (argv[0], mgr, 1); + task1.open (0); + task2.open (0); + task3.open (0); + + while (running_tasks!=0) + { + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) still running tasks\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) result: '%s'\n"), + order.c_str ())); + + if (order != "321123") + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) wrong order of spawns ('%s', should be '321123')\n"), + order.c_str ())); + test_status = 1; + } + +#if !defined (ACE_OPENVMS) + // -------------------------------------------------- + // Finally, try the reactor stuff... + mgr.open (ACE_Process_Manager::DEFAULT_SIZE, + ACE_Reactor::instance ()); + + pid_t child7 = spawn_child (argv[0], + mgr, + 5); + /* pid_t child8 = */ spawn_child (argv[0], + mgr, + 6); + + mgr.register_handler (new Exit_Handler ("specific"), + child7); + + ACE_Time_Value how_long (10); + + ACE_Reactor::instance ()->run_reactor_event_loop (how_long); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Reactor loop done!\n") )); + + size_t nr_procs = mgr.managed (); + if (nr_procs != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) %d processes left in manager\n"), + nr_procs)); +#endif /* !defined (ACE_OPENVMS) */ + ACE_END_TEST; + return test_status; +} + diff --git a/ACE/tests/Process_Manual_Event_Test.cpp b/ACE/tests/Process_Manual_Event_Test.cpp new file mode 100644 index 00000000000..7608ecc5fc2 --- /dev/null +++ b/ACE/tests/Process_Manual_Event_Test.cpp @@ -0,0 +1,259 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Process_Manual_Event_Test.cpp +// +// = DESCRIPTION +// This test verifies the functionality of the <ACE_Manual_Event> +// process-shared implementation. +// +// = AUTHOR +// Martin Corino <mcorino@remedy.nl> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Process.h" +#include "ace/Manual_Event.h" +#include "ace/Time_Value.h" +#include "ace/Get_Opt.h" +#include "ace/ACE.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_unistd.h" +#include "ace/os_include/os_dirent.h" + + +#if (!defined (ACE_LACKS_FORK) || defined (ACE_WIN32)) && \ + (defined (ACE_WIN32) || \ + (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \ + !defined (ACE_LACKS_MUTEXATTR_PSHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \ + defined (ACE_USES_FIFO_SEM) || \ + (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && !defined (ACE_LACKS_NAMED_POSIX_SEM))) +static int iterations = 10; +static int child_process = 0; +static const ACE_TCHAR *event_ping_name = ACE_TEXT ("ACE_Ping_Event"); +static const ACE_TCHAR *event_pong_name = ACE_TEXT ("ACE_Pong_Event"); + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-i #iterations] [-c (child process)]\n"))); + ACE_OS::exit (1); +} + +// Parse the command-line arguments and set options. +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:c")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + child_process = 1; + break; + default: + print_usage_and_die (); + break; + } +} + +static void +acquire_release (void) +{ + ACE_Manual_Event event_ping (0, USYNC_PROCESS, event_ping_name); + ACE_Manual_Event event_pong (0, USYNC_PROCESS, event_pong_name); + + // Make sure the constructor succeeded + ACE_ASSERT (ACE_LOG_MSG->op_status () == 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Begin ping-pong\n"))); + + int i; + if (child_process) + { + for (i = 0; i < iterations; ++i) + { + event_ping.signal (); + + if (event_pong.wait ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) %p\n"), + ACE_TEXT ("Failed acquiring pong"))); + else + { + event_pong.reset (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) Pong\n"))); + } + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) Testing timeouts\n"))); + + // test timed wait + ACE_Time_Value wait = ACE_OS::gettimeofday (); + wait.sec (wait.sec () + 3); // timeout in 3 secs + + if (event_pong.wait (&wait)) + { + if (errno != ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) %p, but expected ETIME\n"), + ACE_TEXT ("event_pong.wait()"))); + } + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) Acquired pong without release()\n"))); + + event_ping.signal (); // release waiting parent before timeout + } + else + { + for (i = 0; i < iterations; ++i) + { + if (event_ping.wait ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) %p\n"), + ACE_TEXT ("Failed acquiring ping"))); + else + { + event_ping.reset (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Ping\n"))); + } + + event_pong.signal (); + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) Testing timeouts\n"))); + + // test timed wait + ACE_Time_Value wait = ACE_OS::gettimeofday (); + wait.sec (wait.sec () + 10); // timeout in 10 secs + + if (event_ping.wait (&wait)) + { + if (errno != ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) %p but should be ETIME\n"), + ACE_TEXT ("Acquire pong"))); + + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) Acquire pong timed out\n"))); + } + } +} +#endif /* ! ACE_LACKS_FORK */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ +#if defined (ACE_LACKS_FORK) && !defined (ACE_WIN32) + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Process_Manual_Event_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("fork is not supported on this platform\n"))); + ACE_END_TEST; +#elif defined (ACE_WIN32) || \ + (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \ + !defined (ACE_LACKS_MUTEXATTR_PSHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \ + defined (ACE_USES_FIFO_SEM) || \ + (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && !defined (ACE_LACKS_NAMED_POSIX_SEM)) + + parse_args (argc, argv); + + // Child process code. + if (child_process) + { + ACE_START_TEST (ACE_TEXT ("Process_Manual_Event_Test-child")); + ACE_OS::sleep (2); + acquire_release (); + ACE_END_LOG; + } + else + { + ACE_START_TEST (ACE_TEXT ("Process_Manual_Event_Test")); + + // The parent cleans up any remnant of past runs of this test. + // See Bugzilla #2662 for further info. + // On AIX, this is done by removing the shared memory objects before + // trying to run. +# if defined (AIX) + if (::shm_unlink (event_ping_name) != 0 && errno != ENOENT) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) event_ping %p\n"), + ACE_TEXT ("shm_unlink"))); + if (::shm_unlink (event_pong_name) != 0 && errno != ENOENT) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) event_pong %p\n"), + ACE_TEXT ("shm_unlink"))); +# endif /* AIX */ + +#if defined (ACE_WIN32) + const ACE_TCHAR *cmdline_format = ACE_TEXT("\"%s\" -c -i %d"); +#elif !defined (ACE_USES_WCHAR) +const ACE_TCHAR *cmdline_format = ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR ACE_TEXT("%s -c -i %d"); +#else +const ACE_TCHAR *cmdline_format = ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR ACE_TEXT("%ls -c -i %d"); +#endif + + ACE_Process_Options options; + options.command_line (cmdline_format, + argv[0], + iterations); + // Spawn a child process that will contend for the + // lock. + ACE_Process child; + + // Spawn the child process. + int result = child.spawn (options); + ACE_ASSERT (result != -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Parent spawned child process with pid = %d.\n"), + child.getpid ())); + + // start test + acquire_release (); + + ACE_exitcode child_status; + // Wait for the child processes we created to exit. + int wait_result = child.wait (&child_status); + ACE_ASSERT (wait_result != -1); + if (child_status == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Child %d finished ok\n"), + child.getpid ())); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Child %d finished with status %d\n"), + child.getpid (), child_status)); + ACE_END_TEST; + } +#else /* !ACE_LACKS_FORK */ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Process_Manual_Event_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Process shared events are not supported on this platform\n"))); + ACE_END_TEST; +#endif /* ! ACE_LACKS_FORK */ + + return 0; +} diff --git a/ACE/tests/Process_Mutex_Test.cpp b/ACE/tests/Process_Mutex_Test.cpp new file mode 100644 index 00000000000..e70cd8adede --- /dev/null +++ b/ACE/tests/Process_Mutex_Test.cpp @@ -0,0 +1,218 @@ +// $Id$ +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Process_Mutex_Test.cpp +// +// = DESCRIPTION +// Tests an <ACE_Process_Mutex> shared between multiple child processes. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Mutex.h" +#include "ace/Process.h" +#include "ace/Process_Mutex.h" +#include "ace/Get_Opt.h" +#include "ace/ACE.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/os_include/os_dirent.h" + +ACE_RCSID(tests, Process_Mutex_Test, "$Id$") + +static int release_mutex = 1; +static int child_process = 0; +static const ACE_TCHAR *mutex_name = ACE_DEFAULT_MUTEX; +#if defined (__Lynx__) +static const u_int n_processes = 4; +#else /* ! __Lynx__ */ +static const u_int n_processes = ACE_MAX_PROCESSES; +#endif /* ! __Lynx__ */ + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-d (don't release mutex)] ") + ACE_TEXT ("[-c (child process)] [-n mutex name]\n"))); + ACE_OS::exit (1); +} + +// Parse the command-line arguments and set options. +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("dcn:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'd': + release_mutex = 0; + break; + case 'c': + child_process = 1; + break; + case 'n': + mutex_name = get_opt.opt_arg (); + break; + default: + print_usage_and_die (); + break; + } +} + +static void +acquire_release (void) +{ + ACE_Process_Mutex mutex (mutex_name); + + // Make sure the constructor succeeded + ACE_ASSERT (ACE_LOG_MSG->op_status () == 0); + + // To see if we really are the only holder of the mutex below, + // we'll try to create a file with exclusive access. If the file + // already exists, we're not the only one holding the mutex. + ACE_TCHAR mutex_check[MAXPATHLEN+1]; + ACE_OS::strncpy (mutex_check, mutex_name, MAXPATHLEN); + ACE_OS::strncat (mutex_check, ACE_TEXT ("_checker"), MAXPATHLEN); + + // Grab the lock + int mutex_acq = mutex.acquire (); + ACE_ASSERT (mutex_acq == 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Mutex acquired %s\n"), + mutex_name)); + + ACE_HANDLE checker_handle = ACE_OS::open (mutex_check, O_CREAT | O_EXCL); + if (checker_handle == ACE_INVALID_HANDLE) + { + ACE_ASSERT (errno != EEXIST); + ACE_DEBUG ((LM_WARNING, ACE_TEXT ("(%P): %p\n"), + ACE_TEXT ("checker file open"))); + } + else + ACE_OS::close (checker_handle); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Working....\n"))); + + // Do some "work", i.e., just sleep for a couple of seconds. + ACE_OS::sleep (2); + + // Free up the check file for the next acquirer. + ACE_OS::unlink (mutex_check); + + // Check if we need to release the mutex + if (release_mutex == 1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Releasing the mutex %s\n"), + mutex_name)); + int mutex_release = mutex.release (); + ACE_ASSERT (mutex_release == 0); + } +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + parse_args (argc, argv); + + // Child process code. + if (child_process) + { + ACE_TCHAR lognm[MAXPATHLEN]; + int mypid (ACE_OS::getpid ()); + ACE_OS::sprintf(lognm, ACE_TEXT ("Process_Mutex_Test-child-%d"), mypid); + + ACE_START_TEST (lognm); + acquire_release (); + ACE_END_LOG; + } + else + { + ACE_START_TEST (ACE_TEXT ("Process_Mutex_Test")); +# if !defined( ACE_HAS_SYSV_IPC) || defined( ACE_USES_MUTEX_FOR_PROCESS_MUTEX ) + // When Process_Mutex is pthreads based, then the owner of mutex destroys it + // in destructor. This may disturb the other processes which still uses the + // mutex. It is safer then to hold the mutex in main process, and destroy it after + // children finish. This is temporary solution, and in future pthread base + // Process_Mutex shall control the destruction of mutex better. + ACE_Process_Mutex mutex( mutex_name ); +# endif + +#if !defined (ACE_WIN32) && defined (ACE_USES_WCHAR) + static const ACE_TCHAR* format = ACE_TEXT ("%ls -c -n %ls%ls"); +#else + static const ACE_TCHAR* format = ACE_TEXT ("%s -c -n %s%s"); +#endif /* !ACE_WIN32 && ACE_USES_WCHAR */ + ACE_Process_Options options; + options.command_line (format, argv[0], mutex_name, + release_mutex == 0 ? ACE_TEXT (" -d") : ACE_TEXT ("")); + + // Spawn <n_processes> child processes that will contend for the + // lock. + ACE_Process children[n_processes]; + size_t i; + + for (i = 0; + i < n_processes; + i++) + { + // Spawn the child process. + if (children[i].spawn (options) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("spawn of client %d failed\n"), + i), + -1); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Parent spawned child process with pid = %d.\n"), + children[i].getpid ())); + } + + // Give the newly spawned child process a chance to start... + // David Levine thinks this sleep() is required because + // calling ::waitpid () before a fork'ed child has actually + // been created may be a problem on some platforms. It's + // not enough for fork() to have returned to the parent. + ACE_OS::sleep (1); + } + + for (i = 0; i < n_processes; i++) + { + ACE_exitcode child_status; + // Wait for the child processes we created to exit. + int wait_result = children[i].wait (&child_status); + ACE_ASSERT (wait_result != -1); + if (child_status == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Child %d finished ok\n"), + children[i].getpid ())); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Child %d finished with status %d\n"), + children[i].getpid (), child_status)); + } + + ACE_END_TEST; + } + + return 0; +} diff --git a/ACE/tests/Process_Semaphore_Test.cpp b/ACE/tests/Process_Semaphore_Test.cpp new file mode 100644 index 00000000000..af08108ad75 --- /dev/null +++ b/ACE/tests/Process_Semaphore_Test.cpp @@ -0,0 +1,230 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Process_Semaphore_Test.cpp +// +// = DESCRIPTION +// Tests an ACE Semaphore shared between multiple child processes. +// +// = AUTHOR +// Martin Corino <mcorino@remedy.nl> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Mutex.h" +#include "ace/Process.h" +#if defined (ACE_WIN32) || \ + defined (ACE_USES_FIFO_SEM) || \ + (defined (ACE_HAS_POSIX_SEM) && !defined (ACE_LACKS_NAMED_POSIX_SEM)) +# include "ace/Time_Value.h" +# include "ace/OS_NS_sys_time.h" +# include "ace/Semaphore.h" +#else +# include "ace/Process_Semaphore.h" +#endif +#include "ace/Get_Opt.h" +#include "ace/ACE.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/os_include/os_dirent.h" + +ACE_RCSID(tests, Process_Semaphore_Test, "Process_Semaphore_Test.cpp,v 4.42 2003/12/26 21:59:35 shuston Exp") + +#if !defined (ACE_LACKS_FORK) +static int iterations = 10; +static int child_process = 0; +static const char *sema_ping_name = "ACE_Ping_Semaphore"; +static const char *sema_pong_name = "ACE_Pong_Semaphore"; + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-i #iterations] [-c (child process)]\n"))); + ACE_OS::exit (1); +} + +// Parse the command-line arguments and set options. +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:c")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'i': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + child_process = 1; + break; + default: + print_usage_and_die (); + break; + } +} + +static void +acquire_release (void) +{ +#if defined (ACE_WIN32) || \ + defined (ACE_USES_FIFO_SEM) || \ + (defined (ACE_HAS_POSIX_SEM) && !defined (ACE_LACKS_NAMED_POSIX_SEM)) + ACE_Semaphore sema_ping (0, USYNC_PROCESS, ACE_TEXT_CHAR_TO_TCHAR (sema_ping_name)); + ACE_Semaphore sema_pong (0, USYNC_PROCESS, ACE_TEXT_CHAR_TO_TCHAR (sema_pong_name)); +#else + ACE_Process_Semaphore sema_ping (0, ACE_TEXT_CHAR_TO_TCHAR (sema_ping_name)); + ACE_Process_Semaphore sema_pong (0, ACE_TEXT_CHAR_TO_TCHAR (sema_pong_name)); +#endif + + // Make sure the constructor succeeded + ACE_ASSERT (ACE_LOG_MSG->op_status () == 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Begin ping-pong\n"))); + + if (child_process) + { + for (int i=0; i<iterations ;++i) + { + sema_ping.release (); + + if (sema_pong.acquire ()) + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P) Failed acquiring pong\n"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Pong\n"))); + } + +#if defined (ACE_WIN32) || \ + defined (ACE_USES_FIFO_SEM) || \ + (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && \ + !defined (ACE_LACKS_NAMED_POSIX_SEM)) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Testing timeouts\n"))); + + // test timed wait + ACE_Time_Value wait = ACE_OS::gettimeofday (); + wait.sec (wait.sec () + 3); // timeout in 3 secs + + if (sema_pong.acquire (wait)) + ACE_ASSERT(errno == ETIME); + else + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P) Acquired pong without release()\n"))); + + sema_ping.release (); // release waiting parent before timeout +#endif + } + else + { + for (int i=0; i<iterations ;++i) + { + if (sema_ping.acquire ()) + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P) Failed acquiring ping\n"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Ping\n"))); + + sema_pong.release (); + } + +#if defined (ACE_WIN32) || \ + defined (ACE_USES_FIFO_SEM) || \ + (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && \ + !defined (ACE_LACKS_NAMED_POSIX_SEM)) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) Testing timeouts\n"))); + + // test timed wait + ACE_Time_Value wait = ACE_OS::gettimeofday (); + wait.sec (wait.sec () + 10); // timeout in 10 secs + + if (sema_ping.acquire (wait)) + { + ACE_ASSERT(errno == ETIME); + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P) Acquiring pong timed out\n"))); + } +#endif + } +} +#endif /* ! ACE_LACKS_FORK */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ +#if defined (ACE_LACKS_FORK) + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("Process_Semaphore_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("fork is not supported on this platform\n"))); + ACE_END_TEST; +#else + + parse_args (argc, argv); + + // Child process code. + if (child_process) + { + ACE_START_TEST (ACE_TEXT ("Process_Semaphore_Test-child")); + acquire_release (); + ACE_END_LOG; + } + else + { + ACE_START_TEST (ACE_TEXT ("Process_Semaphore_Test")); + + ACE_Process_Options options; + options.command_line (ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("Process_Semaphore_Test") + ACE_PLATFORM_EXE_SUFFIX + ACE_TEXT (" -c -i %d"), + iterations); + + // Spawn a child process that will contend for the + // lock. + ACE_Process child; + + // Spawn the child process. + int result = child.spawn (options); + ACE_ASSERT (result != -1); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Parent spawned child process with pid = %d.\n"), + child.getpid ())); + + // start test + acquire_release (); + + ACE_exitcode child_status; + // Wait for the child processes we created to exit. + ACE_ASSERT (child.wait (&child_status) != -1); + if (child_status == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Child %d finished ok\n"), + child.getpid ())); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Child %d finished with status %d\n"), + child.getpid (), child_status)); + + ACE_END_TEST; + } +#endif /* ! ACE_LACKS_FORK */ + + return 0; +} diff --git a/ACE/tests/Process_Strategy_Test.cpp b/ACE/tests/Process_Strategy_Test.cpp new file mode 100644 index 00000000000..f7d2d35bc00 --- /dev/null +++ b/ACE/tests/Process_Strategy_Test.cpp @@ -0,0 +1,708 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Process_Strategy_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_Strategy_Acceptor> and +// <ACE_File_Lock> classes. The <ACE_Strategy_Acceptor> uses +// either the <ACE_Process_Strategy> (which forks a +// process-per-connection and runs as a concurrent server +// process), the <ACE_Thread_Strategy> (which spawns a +// thread-per-connection and runs as a concurrent server thread), +// or <ACE_Reactive_Strategy> (which register the <Svc_Handler> +// with the <Reactor> and runs in the main thread of control as an +// iterative server). This server queries and increments a +// "counting value" in a file. +// +// This test program can be run in the following ways: +// +// # Run the server "reactively" (i.e., iteratively) +// % Process_Strategy_Test -c REACTIVE +// +// # Run the server in multi-threads. +// % Process_Strategy_Test -c THREAD +// +// # Run the server in multi-processes +// % Process_Strategy_Test -c PROCESS +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// and Kevin Boyle <kboyle@sanwafp.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Acceptor.h" +#include "ace/Handle_Set.h" +#include "ace/Get_Opt.h" +#include "ace/Null_Condition.h" +#include "ace/Null_Mutex.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Strategies_T.h" +#include "ace/Singleton.h" +#include "ace/Synch_Traits.h" +#include "ace/File_Lock.h" + +// Counting_Service and Options in here +#include "Process_Strategy_Test.h" + +ACE_RCSID(tests, Process_Strategy_Test, "$Id$") + +// This test does not function properly when fork() is used on HP-UX +#if defined(__hpux) +#define ACE_LACKS_FORK +#endif /* __hpux */ + +#if defined (ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION) + +template ACE_Singleton<Options, ACE_Null_Mutex> * + ACE_Singleton<Options, ACE_Null_Mutex>::singleton_; + +#endif /* ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION */ + +// Define a <Strategy_Acceptor> that's parameterized by the +// <Counting_Service>. + +typedef ACE_Strategy_Acceptor <Counting_Service, ACE_SOCK_ACCEPTOR> ACCEPTOR; + +// Create an Options Singleton. +typedef ACE_Singleton<Options, ACE_Null_Mutex> OPTIONS; + +// counter for connections +static int connections = 0; + +// Use this to show down the process gracefully. +static void +connection_completed (void) +{ + // Increment connection counter. + connections++; + + // If all connections have been serviced. + if (connections == ACE_MAX_ITERATIONS + 1) + // Make sure that the event loop is interrupted. + ACE_Reactor::instance()->wakeup_all_threads (); +} + +// Constructor +Process_Strategy::Process_Strategy (size_t n_processes, + ACE_Event_Handler *acceptor, + ACE_Reactor *r, + int avoid_zombies) + : ACE_Process_Strategy<Counting_Service> (n_processes, + acceptor, + r, + avoid_zombies) +{ +} + +// Destructor. g++ 2.7.2.3 gets very confused ("Internal compiler +// error") without it. + +Process_Strategy::~Process_Strategy (void) +{ +} + +// Overwrite the process creation method to include connection +// counting. + +int +Process_Strategy::activate_svc_handler (Counting_Service *svc_handler, + void *arg) +{ + // Call down to the base class + int result = + ACE_Process_Strategy<Counting_Service>::activate_svc_handler (svc_handler, + arg); + // Connection is now complete + connection_completed (); + + return result; +} + +ACE_File_Lock & +Options::file_lock (void) +{ + return this->file_lock_; +} + +ACE_Concurrency_Strategy <Counting_Service> * +Options::concurrency_strategy (void) +{ + return this->concurrency_strategy_; +} + +const ACE_TCHAR * +Options::filename (void) +{ + return this->filename_; +} + +Options::Options (void) + : + // Choose to use processes by default. +#if !defined (ACE_LACKS_FORK) + concurrency_type_ (PROCESS) +#elif defined (ACE_HAS_THREADS) + concurrency_type_ (THREAD) +#else + concurrency_type_ (REACTIVE) +#endif /* !ACE_LACKS_FORK */ +{ +} + +Options::~Options (void) +{ + delete this->concurrency_strategy_; + this->concurrency_strategy_ = 0; +} + +int +Options::parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("p:c:f:")); + + // - 26 is for the "process_strategy_test_temp" that is appended + if (ACE::get_temp_dir (this->filename_, MAXPATHLEN - 26) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Temporary path too long\n")), + -1); + + ACE_OS::strcat (this->filename_, ACE_TEXT ("process_strategy_test_temp")); + + for (int c; (c = get_opt ()) != -1; ) + switch (c) + { + case 'c': + if (ACE_OS::strcmp (get_opt.opt_arg (), + ACE_TEXT ("REACTIVE")) == 0) + OPTIONS::instance ()->concurrency_type (Options::REACTIVE); +#if !defined (ACE_LACKS_FORK) + else if (ACE_OS::strcmp (get_opt.opt_arg (), + ACE_TEXT ("PROCESS")) == 0) + OPTIONS::instance ()->concurrency_type (Options::PROCESS); +#endif /* !ACE_LACKS_FORK */ +#if defined (ACE_HAS_THREADS) + else if (ACE_OS::strcmp (get_opt.opt_arg (), + ACE_TEXT ("THREAD")) == 0) + OPTIONS::instance ()->concurrency_type (Options::THREAD); +#endif /* ACE_HAS_THREADS */ + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("WARNING: concurrency strategy \"%s\" ") + ACE_TEXT ("is not supported\n"), + get_opt.opt_arg ())); + break; + case 'f': + ACE_OS::strcpy (this->filename_, get_opt.opt_arg ()); + break; + default: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-f (filename)] ") + ACE_TEXT ("[-c (concurrency strategy)]\n%a"), 1)); + /* NOTREACHED */ + } + + // Initialize the file lock. Note that this object lives beyond the + // lifetime of the Acceptor. + if (this->file_lock_.open (this->filename_, + O_RDWR | O_CREAT, + ACE_DEFAULT_FILE_PERMS) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) opening %s on handle %d.\n"), + this->filename_, + this->file_lock_.get_handle ())); + + int count = 0; + + // Store the initial value of the count in the file. + if (ACE_OS::write (this->file_lock_.get_handle (), + (const void *) &count, + sizeof count) != sizeof count) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("write"))); + + // Initialize the Concurrency strategy. + switch (this->concurrency_type_) + { + case Options::PROCESS: +#if !defined (ACE_LACKS_FORK) + ACE_NEW_RETURN (this->concurrency_strategy_, + Process_Strategy (1, + this, + ACE_Reactor::instance (), + 1), // Avoid zombies. + -1); + break; +#else + ACE_ASSERT ("PROCESS invalid on this platform" == 0); +#endif /* !defined (ACE_LACKS_FORK) */ + case Options::THREAD: +#if defined (ACE_HAS_THREADS) + ACE_NEW_RETURN (this->concurrency_strategy_, + ACE_Thread_Strategy<Counting_Service> + (ACE_Thread_Manager::instance (), + THR_NEW_LWP, + 1), + -1); + break; +#else + ACE_ASSERT (!"THREAD invalid on this platform"); +#endif /* !ACE_HAS_THREADS */ + case Options::REACTIVE: + // Settle for the purely Reactive strategy. + ACE_NEW_RETURN (this->concurrency_strategy_, + ACE_Reactive_Strategy<Counting_Service> + (ACE_Reactor::instance ()), + -1); + break; + } + + return 0; +} + +Options::Concurrency_Type +Options::concurrency_type (void) +{ + return this->concurrency_type_; +} + +void +Options::concurrency_type (Options::Concurrency_Type cs) +{ + this->concurrency_type_ = cs; +} + +Counting_Service::Counting_Service (ACE_Thread_Manager *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) creating the Counting_Service\n"))); +} + +// Read the current value from the shared file and return it to the +// client. + +int +Counting_Service::read (void) +{ + ACE_READ_GUARD_RETURN (ACE_File_Lock, ace_mon, OPTIONS::instance ()->file_lock (), -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reading on handle %d.\n"), + OPTIONS::instance ()->file_lock ().get_handle ())); + + int count; + if (ACE_OS::pread (OPTIONS::instance ()->file_lock ().get_handle (), + (void *) &count, + sizeof count, + 0) != sizeof count) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("read")), + -1); + char buf[BUFSIZ]; + + int n = ACE_OS::sprintf (buf, + "count = %d\n", + count); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) count = %d\n"), + count)); + + if (this->peer ().send_n (buf, n) != n) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("send_n")), + -1); + return 0; +} + +// Increment the current value in the shared file by 1. + +int +Counting_Service::inc (void) +{ + ACE_WRITE_GUARD_RETURN (ACE_File_Lock, ace_mon, + OPTIONS::instance ()->file_lock (), -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) incrementing on handle %d.\n"), + OPTIONS::instance ()->file_lock ().get_handle ())); + + int count; + if (ACE_OS::pread (OPTIONS::instance ()->file_lock ().get_handle (), + (void *) &count, + sizeof count, + 0) != sizeof count) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("read")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) incrementing count from %d to %d\n"), + count, + count + 1)); + count++; + + if (ACE_OS::pwrite (OPTIONS::instance ()->file_lock ().get_handle (), + (const void *) &count, + sizeof count, + 0) != sizeof count) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("write")), + -1); + return 0; +} + +// Receive the request from the client and call the appropriate +// operation. + +int +Counting_Service::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + ACE_Time_Value* timeout = 0; +#if defined (__hpux) + // Even though we're in handle_input, there seems to be a + // situation on HP-UX where there is nothing to recv just yet. + // So, we recv() with a timeout and everything works. + ACE_Time_Value hpux_timeout (3); + timeout = &hpux_timeout; +#endif /* __hpux */ + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reading from peer on %d\n"), + this->peer ().get_handle ())); + size_t len; + // Read the PDU length first. + ssize_t bytes = this->peer ().recv ((void *) &len, + sizeof len, + timeout); + if (bytes <= 0) + return -1; + + bytes = this->peer ().recv (buf, len); + + if (bytes <= 0 || buf[0] == (char) EOF) + return -1; + else + { + buf[len] = '\0'; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) %d bytes of input on %d is %*s\n"), + bytes, + this->peer ().get_handle (), + bytes, + ACE_TEXT_CHAR_TO_TCHAR (buf))); + // Read and return the current value in the file. + if (ACE_OS::strncmp (buf, + "read", + 4) == 0) + return this->read (); + // Increment the current value in the file. + else if (ACE_OS::strncmp (buf, "inc", 3) == 0) + return this->inc (); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) no match...\n"))); + return 0; + } +} + +int +Counting_Service::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) handling thread\n"))); + + while (this->handle_input () >= 0) + continue; + + return 0; +} + +int +Counting_Service::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + // Done with another connection. + connection_completed (); + + // Call down to base class + return ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>::handle_close (); +} + +// This method is called back by the <Acceptor> once the client has +// connected and the process is forked or spawned. + +int +Counting_Service::open (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) opening service\n"))); + + if (OPTIONS::instance ()->concurrency_type () == Options::PROCESS) + { + // We need to rerun the event loop here since we ended up here + // due to being fork'd and we can't just return to our context + // because it's in the middle of a different event loop that + // won't behave properly since it's meant to handle connection + // establishment, *not* data transfer. + while (this->handle_input () >= 0) + continue; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) About to exit from the child\n"))); + + // Exit the child. + ACE_OS::exit (0); + } + else if (OPTIONS::instance ()->concurrency_type () == Options::THREAD) + // We need to set this to 0 so that our <shutdown> method doesn't + // try to deregister <this> from the Reactor. + this->reactor (0); + return 0; +} + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +// Execute the client tests. + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = + reinterpret_cast<ACE_INET_Addr *> (arg); + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_DEFAULT_SERVER_HOST); + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connector; + + char buf[BUFSIZ]; + const char *command; + size_t command_len; + size_t i; + + for (i = 0; i < ACE_MAX_ITERATIONS; i++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client iteration %d\n"), + i)); + if (connector.connect (stream, + server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 0); + command = "inc"; + command_len = ACE_OS::strlen (command); + + if (stream.send (4, + &command_len, sizeof command_len, + command, command_len) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send")), + 0); + command = "read"; + command_len = ACE_OS::strlen (command); + + if (stream.send (4, + &command_len, sizeof command_len, + command, command_len) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send")), + 0); + else if (stream.recv (buf, sizeof buf) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("recv")), + 0); + + // ACE_DEBUG ((LM_DEBUG, + // ACE_TEXT ("(%P|%t) client iteration %d, buf = %s\n"), + // i, buf)); + + if (stream.close () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("close")), + 0); + } + + command = "read"; + command_len = ACE_OS::strlen (command); + ssize_t bytes_read = 0; + + if (connector.connect (stream, server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 0); + else if (stream.send (4, + &command_len, sizeof command_len, + command, command_len) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send")), + 0); + else if ((bytes_read = stream.recv (buf, sizeof buf)) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv")), + 0); + else + { + // Null terminate buf to avoid an uninitialized memory read in + // the call to ACE_OS::strrchr (). + buf [bytes_read] = '\0'; + + size_t count = ACE_OS::atoi (ACE_OS::strrchr (ACE_TEXT_CHAR_TO_TCHAR (buf), + ACE_TEXT (' '))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) count = %d\n"), + count)); + // Make sure that the count is correct. + ACE_ASSERT (count == ACE_MAX_ITERATIONS); + } + + if (stream.close () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("close")), + 0); + + // Remove the filename. + ACE_OS::unlink (OPTIONS::instance ()->filename ()); + + return 0; +} + +// Performs the server activities. + +// Have all connections been serviced? + +static int +done (void) +{ + return connections == ACE_MAX_ITERATIONS + 1; +} + +static void * +server (void *) +{ + int result = 0; + ACE_Reactor::instance ()->owner (ACE_Thread::self ()); + + while (!done () && result != -1) + // Run the main event loop. + result = ACE_Reactor::instance ()->handle_events (); + + return 0; +} + +#endif /* !ACE_LACKS_FORK || ACE_HAS_THREADS */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Process_Strategy_Test")); + + if (OPTIONS::instance ()->parse_args (argc, argv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("parse_args")), + -1); + + ACCEPTOR acceptor; + + ACE_INET_Addr server_addr; + + // Bind acceptor to any port and then find out what the port was. + // Note that this implicitly creates the Reactor singleton. + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &), + ACE_Reactor::instance(), + 0, + 0, + OPTIONS::instance ()->concurrency_strategy ()) == -1 + || acceptor.acceptor ().get_local_addr (server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + -1); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + // We're running the client and serve as separate processes. + pid_t pid = ACE::fork (ACE_TEXT ("child"), + 1); // Avoid zombies. + + switch (pid) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"))); + ACE_OS::exit (-1); + /* NOTREACHED */ + case 0: + client (&server_addr); + break; + /* NOTREACHED */ + default: + server (0); + break; + /* NOTREACHED */ + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"))); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + (void *) &server_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"))); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + } + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Process_Strategy_Test.h b/ACE/tests/Process_Strategy_Test.h new file mode 100644 index 00000000000..9278e37404a --- /dev/null +++ b/ACE/tests/Process_Strategy_Test.h @@ -0,0 +1,141 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// = FILENAME +// Process_Strategy_Test.h +// +// = DESCRIPTION +// This file contains the definition of Counting_Service and +// Options. Some compilers need it in a .h file for template +// instantiation (such as AIX C Set ++). +// +// = AUTHOR +// Doug Schmidt <schmidt@cs.wustl.edu> and +// Kevin Boyle <kboyle@sanwafp.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_PROCESS_STRATEGY_TEST_H +#define ACE_TESTS_PROCESS_STRATEGY_TEST_H + +#include "ace/Event_Handler.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/File_Lock.h" +#include "ace/SOCK_Stream.h" +#include "ace/Svc_Handler.h" +#include "ace/Strategies_T.h" + +class Counting_Service : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> + // = TITLE + // Reads and increments the count in a shared file. + // + // = DESCRIPTION + // Objects of this class execute in a separate process as a + // result of the <ACE_Strategy_Acceptor> and + // <ACE_Process_Strategy>. +{ +public: + Counting_Service (ACE_Thread_Manager * = 0); + // Constructor. + + virtual int open (void *v); + // Hook that is used to initialize the service (called by the + // <ACE_Strategy_Acceptor::handle_input> Template Method). + +protected: + // = Methods invoked via "pointer to method" table entry. + + virtual int svc (void); + // Handle the THREAD case. + + // = Operations corresponding to requests from the client. + int read (void); + // Execute the read operation on the file. + + int inc (void); + // Execute the increment operation on the file. + + // = Hooks called by <Reactor> and <Strategy_Acceptor>. + + virtual int handle_input (ACE_HANDLE p = ACE_INVALID_HANDLE); + // Hook called by the <Reactor> when data arrives from the client. + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); + // Closing down +}; + +class Process_Strategy : public ACE_Process_Strategy<Counting_Service> +{ +public: + + // Constructor + Process_Strategy (size_t n_processes = 1, + ACE_Event_Handler *acceptor = 0, + ACE_Reactor * = 0, + int flags = 0); + + // Destructor + ~Process_Strategy (void); + + // Overwrite the process creation method to include connection + // counting + virtual int activate_svc_handler (Counting_Service *svc_handler, + void *arg = 0); +}; + +class Options : public ACE_Event_Handler + // = TITLE + // Maintains the options for this program. +{ +public: + Options (void); + // Constructor. + + ~Options (void); + // Destructor. + + int parse_args (int argc, ACE_TCHAR *argv[]); + // Read command-line arguments and initialize options. + + enum Concurrency_Type + { + PROCESS, // Run the test in separate processes. + REACTIVE, // Run the test reactively in one thread. + THREAD // Run the test as in separate threads. + }; + + // = Get/set concurrency type. + Concurrency_Type concurrency_type (void); + void concurrency_type (Concurrency_Type); + + ACE_File_Lock &file_lock (void); + // Returns the file lock. + + const ACE_TCHAR *filename (void); + // Returns the filename that we're using as the lock. + + ACE_Concurrency_Strategy <Counting_Service> *concurrency_strategy (void); + // Returns the concurrency strategy. + +private: + Concurrency_Type concurrency_type_; + // Concurrency strategy that we're running. + + ACE_File_Lock file_lock_; + // Lock for the counting file. + + ACE_Concurrency_Strategy<Counting_Service> *concurrency_strategy_; + // Activation strategy that either forks a new process or spawns a + // new thread for each client connection. + + ACE_TCHAR filename_[MAXPATHLEN + 1]; + // Name of the counting file. +}; + +#endif /* ACE_TESTS_PROCESS_STRATEGY_TEST_H */ diff --git a/ACE/tests/QtReactor_Test.cpp b/ACE/tests/QtReactor_Test.cpp new file mode 100644 index 00000000000..1891a92de76 --- /dev/null +++ b/ACE/tests/QtReactor_Test.cpp @@ -0,0 +1,934 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// QtReactor_Test.cpp +// +// = DESCRIPTION +// Simple test of QtReactor. Test is intended to verify if QtReactor +// correctly cooperates with Qt event loop in typical application. Test +// creates a number of timers which send datagrams. These datagrams are +// expected by datagram_handlers registered in reactor. Test asynchronously +// establishes also a number of loopback tcp connections using ACE_Acceptors +// and ACE_Connectors. Socket activities are handled asynchronously to ensure +// that reactor does not lose events in traffic transmission. Moreover, test +// registers and removes handlers frequently to cover register/remove_handler +// method of QtReactor which are known (10/07/2004) to be buggy. +// +// Classes: +// QTestApplication - main qt application running event loop for a +// finite time +// Dgram_Handler - responsible for sending and receiving datagrams as +// well as handling timeouts. Datagrams are sent in +// handle_timeout method. +// TCPConnectionHandler - connection handler responsible for sending and +// receiving data using tcp streams. +// TCPAcceptorHandler - acceptor responsible for acceptance and +// registration of connections in HandlersRegister +// class. +// HandlersRegister - register of event_handlers, responsible also for +// the analysis of test results. +// +// = AUTHOR +// Marek Brudka <mbrudka@elka.pw.edu.pl> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID (tests, + QtReactor_Test, + "$Id$") + +#include <assert.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "ace/OS_NS_time.h" +#include "ace/Time_Value.h" +#include "ace/QtReactor/QtReactor.h" +#include "ace/Event_Handler.h" +#include "ace/Acceptor.h" +#include "ace/Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Dgram.h" + +// Qt specific code +#include "QtReactor_Test.h" + +/* + QTestApplication class implementation +*/ + +QTestApplication::QTestApplication (int argc, char *argv[]): + QApplication (argc, argv, FALSE) /* do not enable GUI */ +{ + //FUZZ: disable check_for_lack_ACE_OS + connect (&finishTimer_, SIGNAL (timeout ()), this, SLOT (finishTest ())); + //FUZZ: enable check_for_lack_ACE_OS +} + +void QTestApplication::finishTest () +{ + ACE_OS::exit (); +} + +void QTestApplication::exec (int msec) +{ + finishTimer_.stop (); + if (0 < msec ) + finishTimer_.start (msec, TRUE); + inherited::exec (); +} + + +// maximum time for testing QtReactor (msec) +const int TotalTestTime = 8000; + +// how many handlers for each event handler class should be started ? +#ifndef __QNXNTO__ +const int HandlersNo = 8 ; +#else /* __QNXNTO__ */ +// it seems that Qt 3.1 for NTO 6.2 is compiled with rather small FD_SETSIZE +// Nevertheless, test works fine with native select reactor +const int HandlersNo = 4; +#endif +// base port for sending datagrams +const u_short BaseDgramPort = 5931; + +// port for TCP connections +const u_short BaseTCPPort = 5931; + +// how many datagrams should be sent for a single handler +const int DgramsToSend = 64; + +// how many bytes to send vie TCP +const int TCPBytesToSend = 1024; + +// how many times to reply tcp recv +const int TCPClientPings = 16; + +// total number of bytes in TCP transmission +const int TCPTotalBytesToSend = TCPClientPings * TCPBytesToSend; + +/** + \class DgramHandler + \brief Simple event handler that receives and counts datagrams + as well as sends dgrams using timeouts. +*/ +class DgramHandler: public ACE_Event_Handler +{ +public: + DgramHandler (ACE_Reactor *p_reactor = 0); + virtual ~DgramHandler (); + + //FUZZ: disable check_for_lack_ACE_OS + int open (const ACE_INET_Addr &local, + int protocol_family=ACE_PROTOCOL_FAMILY_INET, + int protocol=0, + int reuse_addr=0); + //FUZZ: enable check_for_lack_ACE_OS + + virtual ACE_HANDLE get_handle () const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask); + virtual int handle_timeout (const ACE_Time_Value ¤t_time, const void *act=0); + int dgramsSent () const; + int dgramsReceived () const; + int timeoutsTriggered () const; + int expectedTriggers () const; + void expectedTriggers (int); +private: + int dgramsSent_; //!< the number of sent datagrams + int dgramsReceived_; //!< the number of received datagrams + int timeoutsTriggered_; + int expectedTriggers_; + ACE_SOCK_Dgram peer_; //!< datagram socket we listen to +}; + +/** + \class TCPConnectionHandler + \brief TCP stream handler for both sides of connection. +*/ +class TCPConnectionHandler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited; +public: + TCPConnectionHandler (bool p_serverSide = false); + virtual ~TCPConnectionHandler (); + + virtual int handle_output (ACE_HANDLE handle); + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0); + //FUZZ: enable check_for_lack_ACE_OS + + int scheduleSend (ACE_Message_Block *); + int sendBuffers (); + int totalReceived () const; + int totalSent () const; +private: + ACE_Message_Block *buffers_; + int totalReceived_; //!< total number of received bytes + int totalSent_; //!< total number of send bytes + bool serverSide_; //!< if true, echo received data + int pingsNo_; //!< number of pings client should make +}; + +typedef ACE_Connector< TCPConnectionHandler, ACE_SOCK_CONNECTOR > TCPConnectorHandler; +class TCPAcceptorHandler; // forward declaration + +/* \class HandlersRegister + \brief The collection of test tools. Here ACE classes bind to Qt application +*/ +class HandlersRegister +{ +public: + HandlersRegister (ACE_Reactor *p_reactor); + virtual ~HandlersRegister (); + int scheduleTimers (const ACE_Time_Value &p_TestTime); //!< schedule ace timers + int registerDgramHandlers (); //!< open dgrams socket and register in reactor + int registerTCPHandlers (); //!< creates TCP acceptors and connector + int analyze () const; //!< analyze results + int analyzeTimeouts () const; //!< analyze triggered timeouts + int analyzeDgrams () const; //!< analyze collected dgrams + int analyzeTCP () const; //!< analyze TCP transmission + int registerTCPServer (TCPConnectionHandler *); + int TCPServersNo () const; //!< return the number of accepted connections + +private: + ACE_Reactor *reactor_; //!< reactor for this application + DgramHandler *DgramHandlers_[ HandlersNo] ;//!< dgram input handlers + int TCPServersNo_; //!< number of accepted connections + TCPConnectionHandler *TCPServers_[ HandlersNo ]; + TCPConnectionHandler *TCPClients_[ HandlersNo ]; + TCPAcceptorHandler *acceptor_; + TCPConnectorHandler *connectors_[ HandlersNo ]; +}; + +class TCPAcceptorHandler : public ACE_Acceptor< TCPConnectionHandler, ACE_SOCK_ACCEPTOR > +{ +public: + typedef ACE_Acceptor< TCPConnectionHandler, ACE_SOCK_ACCEPTOR > inherited; +public: + TCPAcceptorHandler (HandlersRegister *p_handlersRegister); + virtual ~TCPAcceptorHandler (); + virtual int make_svc_handler (TCPConnectionHandler *& sh); + virtual int activate_svc_handler (TCPConnectionHandler * sh); +private: + HandlersRegister *handlersRegister_; +}; + +/* + DgramHandler class implementation +*/ + +DgramHandler::DgramHandler (ACE_Reactor *p_reactor): + ACE_Event_Handler (p_reactor), + dgramsSent_ (0), + dgramsReceived_ (0), + timeoutsTriggered_ (0), + expectedTriggers_ (0) +{ + reference_counting_policy ().value (Reference_Counting_Policy::ENABLED); +} + +DgramHandler::~DgramHandler () +{ + ACE_TRACE ("DgramHandler::~DgramHandler"); +} + +int DgramHandler::open (const ACE_INET_Addr &local, + int protocol_family, + int protocol, + int reuse_addr) +{ + if (0 > peer_.open (local, + protocol_family, + protocol, + reuse_addr)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot oper dgram socket")), + -1); + + return 0; +} + +ACE_HANDLE DgramHandler::get_handle () const +{ + return peer_.get_handle (); +} + +int DgramHandler::handle_input (ACE_HANDLE handle) +{ + ACE_UNUSED_ARG (handle); + int recvBuffer; + ACE_INET_Addr peerAddress; + int result; + + result = peer_.recv (&recvBuffer, sizeof (recvBuffer) , peerAddress); + + if (0 >= result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("While reading datagram from socket")) + , -1); + else + ++dgramsReceived_; + + return 0; +} + +int DgramHandler::handle_timeout (const ACE_Time_Value ¤t_time, const void *act) +{ + ACE_UNUSED_ARG (current_time); + ACE_UNUSED_ARG (act); + int sendBuffer = 0; + + if (++timeoutsTriggered_ >= expectedTriggers_) + reactor ()->cancel_timer (this, 1); + + ACE_SOCK_Dgram socket; + if (-1 == socket.open (ACE_INET_Addr (static_cast< u_short > (0), + static_cast< ACE_UINT32 > (INADDR_ANY)), + ACE_PROTOCOL_FAMILY_INET, 0, 1)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot open socket for sending Qt dgrams"))); + + ACE_INET_Addr peerAddr; + peer_.get_local_addr (peerAddr); + + if (sizeof (sendBuffer) != socket.send (&sendBuffer, + sizeof (sendBuffer), peerAddr)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot send dgram"))); + else + ++dgramsSent_; + + socket.close (); + + return 0; +} + +int DgramHandler::handle_close (ACE_HANDLE handle, ACE_Reactor_Mask close_mask) +{ + if (peer_.get_handle () != handle ) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unknown handle %d DgramHandler::handle_close " + "with mask %x. My handle is %d\n"), + handle, + close_mask, + peer_.get_handle ())); + else + peer_.close (); + + return 0; +} + +int DgramHandler::dgramsSent () const +{ + return dgramsSent_; +} + +int DgramHandler::dgramsReceived () const +{ + return dgramsReceived_; +} + +int DgramHandler::timeoutsTriggered () const +{ + return timeoutsTriggered_; +} + +void DgramHandler::expectedTriggers (int triggers) +{ + expectedTriggers_ = triggers; +} + +int DgramHandler::expectedTriggers () const +{ + return expectedTriggers_; +} + +/* + TCPConnectionHandler class implementation +*/ + +TCPConnectionHandler::TCPConnectionHandler (bool p_serverSide): + buffers_ (0), + totalReceived_ (0), + totalSent_ (0), + serverSide_ (p_serverSide), + pingsNo_ (TCPClientPings) +{ + reference_counting_policy ().value (Reference_Counting_Policy::ENABLED); +} + +TCPConnectionHandler::~TCPConnectionHandler () +{ + ACE_TRACE ("TCPConnectionHandler::~TCPConnectionHandler"); +} + +int TCPConnectionHandler::handle_input (ACE_HANDLE handle) +{ + ACE_UNUSED_ARG (handle); + ACE_Message_Block *buffer = new ACE_Message_Block (TCPBytesToSend); + int bytesReceived = peer_.recv (buffer->wr_ptr (), buffer->space ()); + + if (bytesReceived > 0) + { + totalReceived_ += bytesReceived; + if (serverSide_ || --pingsNo_ > 0) // echo received buffer + { + buffer->wr_ptr (bytesReceived); + int result = scheduleSend (buffer); + if (0 > result) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot schedule TCP reply")), + -1); + } + else + buffer->release (); + + return 0; + } + + if (errno != EWOULDBLOCK) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P:%p (%d)\n"), + ACE_TEXT ("TCPConnectionHandler::handle_input call with no data on handle "), handle), + -1); + + ACE_ERROR ((LM_WARNING, + ACE_TEXT (" (%P:%p (%d)\n"), + ACE_TEXT ("TCPConnectionHandler::handle_input call with no data on handle "), handle)); + + return 0; +} + +int TCPConnectionHandler::handle_output (ACE_HANDLE handle) +{ + ACE_UNUSED_ARG (handle); + if (!buffers_) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPConnectionHandler::handle_output call for empty buffers (%d)\n"), handle)); + if (0 > sendBuffers ()) // socket broken, kill yourself + return -1; + + if (!buffers_) // everything already send, unregister + { + reactor ()->cancel_wakeup (this, + ACE_Event_Handler::WRITE_MASK | ACE_Event_Handler::DONT_CALL); + reactor ()->remove_handler (this, + ACE_Event_Handler::WRITE_MASK | ACE_Event_Handler::DONT_CALL); + } + + return 0; +} + +int TCPConnectionHandler::open (void * ) +{ + int result = inherited::open (); + + if (!serverSide_) + { + ACE_Message_Block *buffer = new ACE_Message_Block (TCPBytesToSend); + char *bufferData = buffer->wr_ptr (); + int i; + + for (i = buffer->size () - 1; i >= 0; --i) + bufferData[ i ] = static_cast< char > (i); + buffer->wr_ptr (buffer->size ()); + if (0 != (scheduleSend (buffer))) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Cannot schedule initial send\n")), + -1); + } + + return result; +} + +int TCPConnectionHandler::scheduleSend (ACE_Message_Block * buffer) +{ + // link buffer to the end of buffers list + if (buffers_) + { + ACE_Message_Block *lastBuffer = buffers_; + while (lastBuffer->cont ()) + lastBuffer = lastBuffer->cont () ; + lastBuffer->cont (buffer); + } + else + buffers_ = buffer; + + if (0 > sendBuffers ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot schedule TCP send.")), + -1); + return 0; +} + +int TCPConnectionHandler::sendBuffers () +{ + int result = 0; + + if (buffers_) + if (0 < (result = peer_.send_n (buffers_))) // remove sent blocks + { + totalSent_ += result; + while (buffers_ && + static_cast< size_t > (result) >= buffers_->length ()) + { + ACE_Message_Block *buffer = buffers_; + result -= buffers_->length (); + buffers_= buffers_->cont (); + buffer->cont (0); + buffer->release (); + } + + if (buffers_) // some buffers were not sent, truncate data + buffers_->rd_ptr (result); + } + + return result; +} + +int TCPConnectionHandler::handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask) +{ + if (peer_.get_handle () != handle ) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unknown handle %d TCPConnectionHandler::handle_close " + "with mask %x. My handle is %d\n"), + handle, + close_mask, + peer_.get_handle ())); + else + peer_.close (); + + return 0; +} + +int TCPConnectionHandler::totalReceived () const +{ + return totalReceived_; +} + +int TCPConnectionHandler::totalSent () const +{ + return totalSent_; +} + + +/* + HandlersRegister class implementation +*/ + +HandlersRegister::HandlersRegister (ACE_Reactor *p_reactor): + reactor_ (p_reactor), + TCPServersNo_ (0) +{ + int i; + + for (i = 0; i < HandlersNo; ++i) + { + // create dgram input handler + DgramHandlers_[ i ] = new DgramHandler (p_reactor); + + TCPServers_[ i ] = 0; + + TCPClients_[ i ] = new TCPConnectionHandler (false); + + connectors_[ i ] =new TCPConnectorHandler (p_reactor, ACE_NONBLOCK); + connectors_[ i ]->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED) ; + } + + acceptor_ = new TCPAcceptorHandler (this); + acceptor_->reactor (p_reactor); +} + +HandlersRegister::~HandlersRegister () +{ + int i; + if (acceptor_) + { + reactor_->remove_handler (acceptor_, ACE_Event_Handler::ALL_EVENTS_MASK); + acceptor_->close (); + acceptor_->remove_reference (); + } + + for (i = 0; i < HandlersNo; ++i) + { + reactor_->cancel_timer (DgramHandlers_[ i ], 1); + reactor_->remove_handler (DgramHandlers_[ i ], + ACE_Event_Handler::ALL_EVENTS_MASK); + DgramHandlers_[ i ]->remove_reference (); + + if (TCPServers_[ i ]) + { + reactor_->remove_handler (TCPServers_[ i ], + ACE_Event_Handler::ALL_EVENTS_MASK); + TCPServers_[ i ]->remove_reference (); + } + + reactor_->remove_handler (connectors_[ i ], + ACE_Event_Handler::ALL_EVENTS_MASK); + connectors_[ i ]->close (); + connectors_[ i ]->remove_reference (); + + if (TCPClients_[ i ]) + { + reactor_->remove_handler (TCPClients_[ i ], + ACE_Event_Handler::ALL_EVENTS_MASK); + TCPClients_[ i ]->remove_reference (); + } + } +} + +int HandlersRegister::TCPServersNo () const +{ + return TCPServersNo_; +} + +int HandlersRegister::scheduleTimers (const ACE_Time_Value &p_TestTime) +{ + int i; + + for (i = 0; i < HandlersNo; ++i) + { + if (-1 == reactor_->schedule_timer (DgramHandlers_[ i ], + (const void *) 0, + ACE_Time_Value::zero, + p_TestTime * (0.5 / DgramsToSend))) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot schedule ACE timer")), + -1); + + DgramHandlers_[ i ] ->expectedTriggers (DgramsToSend); + + } + + return 0; +} + +int HandlersRegister::registerDgramHandlers () +{ + int i; + + // open dgram handlers for all ports + for (i = 0; i < HandlersNo; ++i) + if (-1 == DgramHandlers_[ i ]->open ( + ACE_INET_Addr (i + BaseDgramPort, + ACE_TEXT ("127.0.0.1"), + ACE_PROTOCOL_FAMILY_INET))) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot open dgram handler")), + -1); + + // register dgram handlers + for (i = 0; i < HandlersNo; ++i) + if (-1 == reactor_->register_handler (DgramHandlers_[ i ], + ACE_Event_Handler::READ_MASK)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot register dgram handler")), + -1); + return 0; +} + +int HandlersRegister::registerTCPHandlers () +{ + ACE_INET_Addr addr (BaseTCPPort); + + if (-1 == acceptor_->open (addr, reactor_, 1)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p \n"), + ACE_TEXT ("Cannot open acceptor port")), + -1); + + int i; + addr.set (BaseTCPPort, ACE_TEXT ("127.0.0.1")); + + for (i = 0; i < HandlersNo; ++i) + { + if (-1 == connectors_[ i ]->connect ( + TCPClients_[ i ], + addr, + ACE_Synch_Options::asynch)) + if (errno != EWOULDBLOCK ) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P) %p (%d)\n"), + ACE_TEXT ("Cannot connect connector"), + i), + -1); + } + + return 0; +} + +int HandlersRegister::registerTCPServer (TCPConnectionHandler *handler) +{ + if (TCPServersNo_ < HandlersNo) + { + TCPServers_[ TCPServersNo_++ ] = handler; + return 0; + } + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Too many servers registered (%d). ACE_Reactor or ACE_Acceptor broken?\n"), + handler->get_handle ())); + return -1; +} + +int HandlersRegister::analyze () const +{ + int result = 0; + + if (0 > analyzeTimeouts ()) + result = -1; + + if (0 > analyzeDgrams ()) + result = -1; + + if (0 > analyzeTCP ()) + result = -1; + + return result; +} + +int HandlersRegister::analyzeTimeouts () const +{ + int i; + int result = 0; + + for (i = 0; i < HandlersNo; ++i) + if (DgramHandlers_[ i ]->expectedTriggers () != + DgramHandlers_[ i ]->timeoutsTriggered ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Dgram_Handlers (%d) expected %d timeouts" + " but triggered only %d.\n"), + i, + DgramHandlers_[ i ]->expectedTriggers (), + DgramHandlers_[ i ]->timeoutsTriggered ())); + result = -1; + } + + return result; +} + +int HandlersRegister::analyzeDgrams () const +{ + int result = 0; + int i; + + for (i = 0; i < HandlersNo; ++i) + if (DgramHandlers_[ i ]->dgramsSent () != + DgramHandlers_[ i ]->dgramsReceived ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("DgramsHandler (%d) sent %d dgrams but received only %d." + "Either reactor failed or system lost local dgrams..\n"), + DgramHandlers_[ i ]->dgramsSent (), + DgramHandlers_[ i ]->dgramsReceived ())); + result = -1 ; + } + + return result; +} + +int HandlersRegister::analyzeTCP () const +{ + int i; + int result = 0; + + for (i = 0; i < HandlersNo; ++i) + { + if (TCPClients_[ i ]->totalSent () != TCPTotalBytesToSend ) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPClient (%d): wanted to send %d but sent only (%d).\n"), + i, + TCPTotalBytesToSend, + TCPClients_[ i ]->totalSent ())); + result = -1; + } + + if (TCPClients_[ i ]->totalReceived () != TCPTotalBytesToSend ) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPClient (%d): expected %d bytes but received only (%d).\n"), + i, + TCPTotalBytesToSend, + TCPClients_[ i ]->totalReceived ())); + result = -1; + } + + if (TCPServers_[ i ]) + { + if (TCPServers_[ i ]->totalSent () != TCPTotalBytesToSend ) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPServer (%d): wanted to send %d bytes " + "but sent only (%d).\n"), + i, + TCPTotalBytesToSend, + TCPServers_[ i ]->totalSent ())); + result = -1; + } + + if (TCPServers_[ i ]->totalReceived () != TCPTotalBytesToSend ) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPServer (%d): expected %d bytes but received only (%d).\n"), + i, + TCPTotalBytesToSend, + TCPServers_[ i ]->totalReceived ())); + result = -1; + } + + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPServer (%d): not connected.\n"), + i)); + result = -1; + } + } + + return result; +} + + +/* + TCPAcceptorHandler class implementation +*/ + +TCPAcceptorHandler::TCPAcceptorHandler (HandlersRegister *p_handlersRegister): + handlersRegister_ (p_handlersRegister) +{ + reference_counting_policy ().value (Reference_Counting_Policy::ENABLED); +} + +int TCPAcceptorHandler::make_svc_handler (TCPConnectionHandler *& sh) +{ + sh = new TCPConnectionHandler (true); + sh->reactor (reactor ()); + if (handlersRegister_->TCPServersNo () >= HandlersNo) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("TCPAcceptorHandler::make_svc_handler called to many times!\n"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("TCPAcceptorHandler::make_svc_handler new TCP server created\n"))); + + return 0; +} + +int TCPAcceptorHandler::activate_svc_handler (TCPConnectionHandler * sh) +{ + if (0 == inherited::activate_svc_handler (sh) ) + { + if (0 != handlersRegister_->registerTCPServer (sh)) // for analysis + { + // release event handler + reactor ()->remove_handler (sh, ACE_Event_Handler::ALL_EVENTS_MASK); + sh->remove_reference (); + // report error + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Cannot register server TCPConnectionHandler\n")), + -1); + } + + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Failed to create server TCPConnectionHandler\n")), + -1); + + return 0; +} + +TCPAcceptorHandler::~TCPAcceptorHandler () +{ + ACE_TRACE ("TCPAcceptorHandler::~TCPAcceptorHandler"); +} + +void testNativeReactor (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Testing autotest using native reactor\n"))); + + ACE_Reactor reactor; + HandlersRegister handlersRegister (&reactor); + ACE_Time_Value testTime (TotalTestTime / 1000, (TotalTestTime % 1000) * 1000); + + if (0 <= handlersRegister.scheduleTimers (testTime) && + 0 <= handlersRegister.registerDgramHandlers () && + 0 <= handlersRegister.registerTCPHandlers ()) + { + reactor.run_reactor_event_loop (testTime); + + if (0 != handlersRegister.analyze ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test failed for native reactor. " + "Fix QtReactor_Test or ACE_Reactor.\n"))); + else + ACE_ERROR ((LM_INFO, ACE_TEXT ("Test seems to work with native reactor.\n"))); + } +} + +void testQtReactor (int argc, ACE_TCHAR *argv[]) +{ + // Qt specific code + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Testing QtReactor\n"))); + + QTestApplication app (argc, argv); + ACE_QtReactor qtReactor (&app); + ACE_Reactor reactor (&qtReactor); + HandlersRegister handlersRegister (&reactor); + ACE_Time_Value testTime (TotalTestTime / 1000, (TotalTestTime % 1000) * 1000); + + if (0 <= handlersRegister.scheduleTimers (testTime) && + 0 <= handlersRegister.registerDgramHandlers () && + 0 <= handlersRegister.registerTCPHandlers ()) + { + + app.exec (TotalTestTime); + + if (0 != handlersRegister.analyze ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("QtReactor_Test failed.\n"))); + else + ACE_ERROR ((LM_INFO, ACE_TEXT ("QtReactor_Test passed.\n"))); + } +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("QtReactor_Test")); + + testNativeReactor (argc, argv); + testQtReactor (argc, argv); + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/QtReactor_Test.h b/ACE/tests/QtReactor_Test.h new file mode 100644 index 00000000000..6996ad274b1 --- /dev/null +++ b/ACE/tests/QtReactor_Test.h @@ -0,0 +1,23 @@ +/* -*- C++ -*- */ +// $Id$ +#ifndef QTREACTOR_TEST_H +#define QTREACTOR_TEST_H + +#include <qapplication.h> +#include <qtimer.h> + +class QTestApplication: public QApplication +{ + Q_OBJECT +public: + typedef QApplication inherited; +public: + QTestApplication( int argc, char *argv[] ); + virtual void exec( int msec = 0 ); +public slots: + virtual void finishTest(); //!< slot to finish the test, connected to finishTimer_ +private: + QTimer finishTimer_; //!< timer to finish the test +}; + +#endif /*QTREACTOR_TEST_H*/ diff --git a/ACE/tests/RB_Tree_Test.cpp b/ACE/tests/RB_Tree_Test.cpp new file mode 100644 index 00000000000..1b3658d72b1 --- /dev/null +++ b/ACE/tests/RB_Tree_Test.cpp @@ -0,0 +1,805 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// RB_Tree_Test.cpp +// +// = DESCRIPTION +// This is a test to verify and illustrate the use of the +// <ACE_RB_Tree ACE_RB_Tree_Iterator> and +// <ACE_RB_Tree_Reverse_Iterator> classes. Two different key and +// item types are used in order to demonstrate specialization of +// the <ACE_Less_Than> comparison function object template: int +// (for which the native < operator is sufficient), and const char +// * (for which < operator semantics must be replaced by strcmp +// semantics). An RB tree for each of the four possible type +// parameter permutations over int and const char * is constructed +// and filled in, and the resulting order is checked via an +// iterator over each. +// +// = AUTHOR +// Chris Gill <cdgill@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" /* Include first to enable ACE_ASSERT. */ +#include "ace/RB_Tree.h" +#include "ace/SString.h" +#include "ace/Null_Mutex.h" + +#include "RB_Tree_Test.h" + +ACE_RCSID(tests, RB_Tree_Test, "$Id$") + +// Type definitions for the four distinct parameterizations of the +// test. + +typedef ACE_RB_Tree_Test<int, int, ACE_Less_Than<int>, ACE_Null_Mutex> INT_INT_RB_TREE_TEST; +typedef ACE_RB_Tree_Test<int, const char *, ACE_Less_Than<int>, ACE_Null_Mutex> INT_STR_RB_TREE_TEST; +typedef ACE_RB_Tree_Test<const char *, int, ACE_Less_Than<const char *>, ACE_Null_Mutex> STR_INT_RB_TREE_TEST; +typedef ACE_RB_Tree_Test<const char *, const char *, ACE_Less_Than<const char *>, ACE_Null_Mutex> STR_STR_RB_TREE_TEST; + +// Number of entries placed in each tree. +static int RB_TREE_TEST_ENTRIES = 8; + +// These arrays of numbers as ints and character strings are used to +// instantiate key and item arrays in the tests. +static const char *number_strings [] = +{ + "10", "20", "30", "40", "50", "60", "70", "80" +}; + +static int number_integers [] = +{ + 10, 20, 30, 40, 50, 60, 70, 80 +}; + +// These arrays of ints are used to shuffle the order of insertion and +// deletaion of keys and items for the various tests. +static int int_int_index [] = {0, 1, 2, 3, 4, 5, 6, 7}; // LR inorder +static int int_str_index [] = {7, 6, 5, 4, 3, 2, 1, 0}; // RL inorder +static int str_int_index [] = {4, 6, 2, 7, 5, 3, 1, 0}; // RL BFS +static int str_str_index [] = {4, 2, 1, 0, 3, 6, 5, 7}; // LR preorder + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("RB_Tree_Test")); + + // Construct and run four distinctly parameterized tests. Note that + // specialization of the ACE_Less_Than template for character + // strings performs strcmp style string comparisons rather than < + // operator comparison of the const char * pointers themselves. + + INT_INT_RB_TREE_TEST int_int_test (RB_TREE_TEST_ENTRIES, + number_integers, + number_integers, + int_int_index); + INT_STR_RB_TREE_TEST int_str_test (RB_TREE_TEST_ENTRIES, + number_integers, + number_strings, + int_str_index); + STR_INT_RB_TREE_TEST str_int_test (RB_TREE_TEST_ENTRIES, + number_strings, + number_integers, + str_int_index); + STR_STR_RB_TREE_TEST str_str_test (RB_TREE_TEST_ENTRIES, + number_strings, + number_strings, + str_str_index); + int_int_test.run_test (); + int_str_test.run_test (); + str_int_test.run_test (); + str_str_test.run_test (); + +// ======= Stress Test contributed by Klaus H. Wolf <hw@cyland.com> ========= + + ACE_RB_Tree<ACE_CString, int, ACE_Less_Than<ACE_CString>, ACE_Null_Mutex> tree; + + tree.bind (ACE_CString ("51"), 1); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("13"), 2); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("36"), 3); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("15"), 4); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("22"), 5); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("25"), 6); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("42"), 7); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("48"), 8); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("03"), 9); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("56"), 10); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("28"), 11); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("55"), 12); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("21"), 13); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("62"), 14); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("18"), 15); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("20"), 16); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("26"), 17); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("29"), 18); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("50"), 19); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("05"), 20); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("59"), 21); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("65"), 22); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("66"), 23); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("45"), 24); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("34"), 25); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("27"), 26); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("40"), 27); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("30"), 28); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("64"), 29); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("11"), 30); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("16"), 31); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("47"), 32); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("10"), 33); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("37"), 34); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("09"), 35); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("54"), 36); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("23"), 37); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("44"), 38); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("19"), 39); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("00"), 40); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("04"), 41); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("63"), 42); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("08"), 43); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("39"), 44); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("31"), 45); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("02"), 46); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("33"), 47); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("60"), 48); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("61"), 49); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("57"), 50); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("43"), 51); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("46"), 52); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("38"), 53); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("01"), 54); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("12"), 55); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("24"), 56); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("52"), 57); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("07"), 58); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("14"), 59); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("06"), 60); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("58"), 61); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("49"), 62); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("17"), 63); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("53"), 64); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("32"), 65); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("35"), 66); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.bind (ACE_CString ("41"), 67); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + + tree.unbind (ACE_CString ("51")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("13")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("36")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("15")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("22")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("25")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("42")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("48")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("03")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("56")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("28")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("55")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("21")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("62")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("18")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("20")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("26")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("29")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("50")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("05")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("59")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("65")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("66")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("45")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("34")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("27")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("40")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("30")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("64")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("11")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("16")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("47")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("10")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("37")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("09")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("54")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("23")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("44")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("19")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("00")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("04")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("63")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("08")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("39")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("31")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("02")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("33")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("60")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("61")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("57")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("43")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("46")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("38")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("01")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("12")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("24")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("52")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("07")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("14")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("06")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("58")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("49")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("17")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("53")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("32")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("35")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + tree.unbind (ACE_CString ("41")); + if (0 != tree.test_invariant ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Invariant failure at line %l\n"))); + +// ======== End Stress Test =================== + + ACE_END_TEST; + return 0; +} + +// Constructor. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::ACE_RB_Tree_Test + (int entry_count, + EXT_ID key_array [], + INT_ID item_array [], + int order_index []) + : stable_fwd_iter_ (stable_tree_), + stable_rev_iter_ (stable_tree_), + deprecated_fwd_iter_ (deprecated_tree_), + deprecated_rev_iter_ (deprecated_tree_), + entry_count_ (entry_count), + key_array_ (key_array), + item_array_ (item_array), + order_index_ (order_index) +{ +} + +// Destructor. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::~ACE_RB_Tree_Test (void) +{ +} + +// Run the interface and iteration tests. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> void +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::run_test (void) +{ + // Run the individual portions of the test, in order. + + test_tree_insertion (); + test_post_insertion_iteration (); + test_partial_iteration(); + test_tree_deletion (); + test_post_deletion_iteration (); +} + + +// Tests stable and deprecated insertion interfaces. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> void +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::test_tree_insertion (void) +{ + // Fill in each tree with the key and item from the appropriate + // arrays, using the shuffle index to create the appropriate + // insertion orders. Then, make sure the inserted item can be found + // using the insertion key. + + for (int i = 0; i < entry_count_; ++i) + { + INT_ID item; + int k = order_index_ [i]; + if (!(k >= 0 && k < entry_count_)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Assert failure at line %l\n"))); + + // Test the new stable ACE_Hash_Map_Manager_Ex compliant interface. + if (0 != stable_tree_.bind (key_array_ [k], item_array_ [k])) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable bind %p\n"), + ACE_TEXT ("failure"))); + + if (0 != stable_tree_.find (key_array_ [k], item) || + item != item_array_ [k]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable find %p\n"), + ACE_TEXT ("failure"))); + + // Test the deprecated interface. + if (0 == deprecated_tree_.insert (key_array_ [k], item_array_ [k])) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated insert %p\n"), + ACE_TEXT ("failure"))); + + if (0 == deprecated_tree_.find (key_array_ [k]) || + *deprecated_tree_.find (key_array_ [k]) != item_array_ [k]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated find %p\n"), + ACE_TEXT ("failure"))); + } +} + +// Tests forward and reverse iteration after insertion in both trees. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> void +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::test_post_insertion_iteration (void) +{ + // Reset iterators. + + stable_fwd_iter_ = stable_tree_.begin (); + stable_rev_iter_ = stable_tree_.rbegin (); + deprecated_fwd_iter_ = deprecated_tree_.begin (); + deprecated_rev_iter_ = deprecated_tree_.rbegin (); + + // Iterate over each of the trees, making sure their entries are in + // the same relative order (i.e., the integers and strings represent + // the same values at each respective position in the tree). + for (int i = 0; i < entry_count_; ++i) + { + INT_ID item; + + item = (*stable_fwd_iter_).item (); + if (item != item_array_ [i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + item = (*stable_rev_iter_).item (); + if (item != item_array_ [entry_count_ - i - 1]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable rev iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + item = (*deprecated_fwd_iter_).item (); + if (item != item_array_ [i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated fwd iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + item = (*deprecated_rev_iter_).item (); + if (item != item_array_ [entry_count_ - i - 1]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated rev iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + // Advance each iterator. + ++stable_fwd_iter_; + ++stable_rev_iter_; + ++deprecated_fwd_iter_; + ++deprecated_rev_iter_; + } + + // Advance each iterator again - should be a no-op. + ++stable_fwd_iter_; + ++stable_rev_iter_; + ++deprecated_fwd_iter_; + ++deprecated_rev_iter_; + + + // Make sure each item in each tree has been visited + if (stable_fwd_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter not done but should be\n"))); + if (stable_rev_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter not done but should be\n"))); + if (deprecated_fwd_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter not done but should be\n"))); + if (deprecated_rev_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter not done but should be\n"))); +} + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> void +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::test_partial_iteration(void) +{ + ACE_RB_Tree_Node<EXT_ID, INT_ID> *tree_node = 0; + + stable_tree_.find(key_array_ [2], tree_node); + part_rev_iter_ = ACE_RB_Tree_Reverse_Iterator<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK> (stable_tree_, tree_node); + for (int i=2; i >= 0 ; --i) + { + INT_ID item; + + item = (*part_rev_iter_).item (); + if (item != item_array_ [i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Partial rev iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + part_rev_iter_++; + } + + part_fwd_iter_ = ACE_RB_Tree_Iterator<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK> (key_array_ [5], stable_tree_); + for (int k = 5; k < entry_count_; ++k) + { + INT_ID item; + + item = (*part_fwd_iter_).item (); + if (item != item_array_ [k]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Partial fwd iter, pass %d %p\n"), + k, + ACE_TEXT ("failure"))); + part_fwd_iter_++; + } +} + +// Tests stable and deprecated deletion interfaces. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> void +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::test_tree_deletion (void) +{ + // Remove the even numbered entries from each of the trees. + + for (int i = 0; i < entry_count_; i += 2) + { + // Test the new stable ACE_Hash_Map_Manager_Ex compliant + // interface. + if (stable_tree_.unbind (key_array_ [i]) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable tree, failure pass %d %p\n"), + i, + ACE_TEXT ("unbind"))); + + // Test the deprecated interface. + if (deprecated_tree_.remove (key_array_ [i]) != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated tree, failure pass %d %p\n"), + i, + ACE_TEXT ("remove"))); + } +} + +// Tests forward and reverse iteration after deletions in both trees. + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> void +ACE_RB_Tree_Test<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK>::test_post_deletion_iteration (void) +{ + // Reset iterators + + stable_fwd_iter_ = stable_tree_.begin (); + stable_rev_iter_ = stable_tree_.rbegin (); + deprecated_fwd_iter_ = deprecated_tree_.begin (); + deprecated_rev_iter_ = deprecated_tree_.rbegin (); + + // Iterate over each of the trees, making sure their entries are + // still in the same relative order (i.e., the integers and strings + // represent the same values at each respective position in the + // tree). + for (int i = 1; i < entry_count_; i += 2) + { + INT_ID item; + + item = (*stable_fwd_iter_).item (); + if (item != item_array_ [i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + item = (*stable_rev_iter_).item (); + if (item != item_array_ [entry_count_ - i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable rev iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + item = (*deprecated_fwd_iter_).item (); + if (item != item_array_ [i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated fwd iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + item = (*deprecated_rev_iter_).item (); + if (item != item_array_ [entry_count_ - i]) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated rev iter, pass %d %p\n"), + i, + ACE_TEXT ("failure"))); + + // Advance each iterator via postfix increment. + stable_fwd_iter_++; + stable_rev_iter_++; + deprecated_fwd_iter_++; + deprecated_rev_iter_++; + } + + // Make sure each item in each tree has been visited a second time. + if (stable_fwd_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable fwd iter not done but should be\n"))); + if (stable_rev_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Stable rev iter not done but should be\n"))); + if (deprecated_fwd_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated fwd iter not done but should be\n"))); + if (deprecated_rev_iter_.done () != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Deprecated rev iter not done but should be\n"))); +} + + diff --git a/ACE/tests/RB_Tree_Test.h b/ACE/tests/RB_Tree_Test.h new file mode 100644 index 00000000000..4f20674a17f --- /dev/null +++ b/ACE/tests/RB_Tree_Test.h @@ -0,0 +1,112 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// RB_Tree_Test.cpp +// +// = DESCRIPTION +// Header file for a test to verify and illustrate the use of the +// ACE_RB_Tree ACE_RB_Tree_Iterator, and +// ACE_RB_Tree_Reverse_Iterator classes. +// iterator over each. +// +// = AUTHOR +// Chris Gill <cdgill@cs.wustl.edu> +// +// ============================================================================ + +template <class EXT_ID, class INT_ID, class COMPARE_KEYS, class ACE_LOCK> +class ACE_RB_Tree_Test +{ + // = TITLE + // Implements a templatized test class for the RB_Tree ADT and its + // iterators. + // + // = DESCRIPTION + + // To run the test class on a particular type instantiation of the + // RB_Tree, simply instantiate the test class template with the + // same type parameters, and invoke the run_test method. +public: + // = Traits + + typedef ACE_RB_Tree<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK> + TREE; + typedef ACE_RB_Tree_Iterator<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK> + ITERATOR; + typedef ACE_RB_Tree_Reverse_Iterator<EXT_ID, INT_ID, COMPARE_KEYS, ACE_LOCK> + REVERSE_ITERATOR; + + // = Initialization and termination methods. + + ACE_RB_Tree_Test (int entry_count, + EXT_ID key_array [], + INT_ID item_array [], + int order_index []); + // Constructor. + + ~ACE_RB_Tree_Test (void); + // Destructor. + + void run_test (void); + // Run the individual interface and iteration tests in order. + +private: + + void test_tree_insertion (void); + // Tests stable and deprecated insertion interfaces. + + void test_post_insertion_iteration (void); + // Tests forward and reverse iteration after insertion in both + // trees. + + void test_partial_iteration(void); + //Tests forward and reverse partial iteration + + void test_tree_deletion (void); + // Tests stable and deprecated deletion interfaces. + + void test_post_deletion_iteration (void); + // Tests forward and reverse iteration after deletions in both + // trees. + + TREE stable_tree_; + // Tree for testing stable interface. + + ITERATOR stable_fwd_iter_; + // Forward iterator for tree for testing stable interface. + + ITERATOR part_fwd_iter_; + + REVERSE_ITERATOR part_rev_iter_; + + REVERSE_ITERATOR stable_rev_iter_; + // Forward iterator for tree for testing stable interface. + + TREE deprecated_tree_; + // Tree for testing deprecated interface. + + ITERATOR deprecated_fwd_iter_; + // Forward iterator for tree for testing deprecated interface. + + REVERSE_ITERATOR deprecated_rev_iter_; + // Forward iterator for tree for testing deprecated interface. + + int entry_count_; + // Number of entries in the key, item, and index arrays. + + EXT_ID *key_array_; + // Array of EXT_IDs (keys) with which to test. + + INT_ID *item_array_; + // Array of INT_IDs (items) with which to test. + + int *order_index_; + // Order of indices in the key and item arrays. + +}; diff --git a/ACE/tests/README b/ACE/tests/README new file mode 100644 index 00000000000..17476a3ae5e --- /dev/null +++ b/ACE/tests/README @@ -0,0 +1,82 @@ +$Id$ + +This directory contains a battery of tests that exercise many +capabilities of ACE on the numerous platforms upon which it runs. In +addition to providing an automated regression testing facility, these +tests also illustrate how to program many of the ACE features. The +tests can be executed in a single pass via the perl script +run_test.pl. + +By default, all the output from the tests will be stored in separate +files in the ./log/ (or .\log\) directory. However, you can override +this by setting the ACE_TEST_DIR environment variable to another +prefix. + +________________________________________ + +Please follow these directions when adding a new test to +$ACE_ROOT/tests: + +1. Use appropriate ACE_START_TEST and ACE_END_TEST macros in main () + +2. Add new project entry to the tests.mpc file. + +3. Add test to Makefile. * + +4. Add a new MS project file, e.g., <test>.dsp, then add and entry for + it to the tests.dsw file. * + +5. Add test to run_test.lst. + +* Steps 3 and 4 will go away once the mpc architecture is fully integrated, + since the Makefile and project files can be created on-the-fly by mpc. +________________________________________ + + +The tests have been run on a wide range of platforms (e.g., UNIX, +pSOS, VxWorks, LynxOS, Windows NT/2000/95/98/etc.) and they all work +with the following exceptions: + +1. UPIPE_Test and SPIPE_Test on Windows 95: these tests don't run on + Windows 95 since Windows 95 does not support the server side + functionality of accepting connections from clients using named pipes. + +2. Proactor_Test (to be added) on Windows 95: this test does not run on + Windows 95 since Windows 95 does not support I/O completion ports. + +3. Of the tests (believed) relevant to the single threaded port to pSOSim, + Sigset_Ops_Test, Sock_Connector_Test, Priority_Reactor_Test, + Process_Strategy_Test, and Service_Config_Test are still failing. + These are being debugged and will either be corrected or, if there + are unsupported features in these tests, removed from the test suite for + pSOSim. + +4. The set of tests for pSOSim will be expanded to include those that require + multiple threads, with the release of the multi-threaded port to pSOSim. + +Notes: + +1. Each test creates a log file and writes it to the ./log/ + directory. The default log directory can be changed in the test_config.h + file. + +2. Each log file contains a time stamp of when the test began and also + a time stamp indicating when the test ended. If the ending time stamp + is missing, you can assume that the test did not succeed. + +3. None of the tests require any command line parameters. This is in + accordance with the keeping the test-suite a "one-button" test. If + any of the tests require any variable parameters these are + specified in test_config.h. + +4. Time_Service_Test executes the Time Server and Clerk components as + two processes and so the executable "main" need to be present in + the netsvcs/servers directory. Be sure you build + $ACE_ROOT/netsvcs/lib/ and $ACE_ROOT/netsvcs/servers before running + this test. These components rely on config files. Two sample + config files are also present in the test-suite, namely + UNIXserver.conf and UNIXclerk.conf (and for Win32, Win32server.conf + and Win32clerk.conf). + +If you have any questions/suggestions, please send email to +ace-users@cs.wustl.edu. diff --git a/ACE/tests/Reactor_Dispatch_Order_Test.cpp b/ACE/tests/Reactor_Dispatch_Order_Test.cpp new file mode 100644 index 00000000000..6c700ffb043 --- /dev/null +++ b/ACE/tests/Reactor_Dispatch_Order_Test.cpp @@ -0,0 +1,223 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Dispatch_Order_Test.cpp +// +// = DESCRIPTION +// This is a simple test that checks the order of dispatching of +// ACE Reactors. Order should be: timeout, output, and then input. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Pipe.h" +#include "ace/ACE.h" + +ACE_RCSID(tests, Reactor_Dispatch_Order_Test, "$Id$") + +static const char *message = +"Hello there! Hope you get this message"; + +class Handler : public ACE_Event_Handler +{ +public: + Handler (ACE_Reactor &reactor); + + ~Handler(); + + int handle_timeout (const ACE_Time_Value &tv, + const void *arg); + + int handle_input (ACE_HANDLE fd); + + int handle_output (ACE_HANDLE fd); + + // We need to add MSG_OOB data transfer to this test to check the + // order of when <handle_exception> gets called. I tried with + // Windows 2000 but only one byte of the message came across as + // urgent data. The rest of the data was treated as normal! There + // was some explanation of Microsoft's TCP/IP deals with out-of-band + // data in "Out-of-Band Data and Push Bit in TCP/IP" in the MSDN + // library. However, I did not comprehend that well enough. If + // someone can make this work, please check the order of + // <handle_exception> getting called. + // int handle_exception (ACE_HANDLE fd); + + ACE_Pipe pipe_; + + int dispatch_order_; + bool ok_; // Constructed and initialized ok +}; + +Handler::Handler (ACE_Reactor &reactor) + : ACE_Event_Handler (&reactor), + dispatch_order_ (1), + ok_ (false) +{ + // Create the pipe. + if (0 != this->pipe_.open ()) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("pipe"))); + else + { + // Register for all events. + if (0 != this->reactor ()->register_handler + (this->pipe_.read_handle (), + this, + ACE_Event_Handler::ALL_EVENTS_MASK)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("register"))); + else + this->ok_ = true; + } +} + +Handler::~Handler (void) +{ + this->pipe_.close (); +} + +int +Handler::handle_timeout (const ACE_Time_Value &, + const void *) +{ + int me = this->dispatch_order_++; + if (me != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("handle_timeout should be #1; it's %d\n"), + me)); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_timeout\n"))); + + return 0; +} + +int +Handler::handle_output (ACE_HANDLE) +{ + int me = this->dispatch_order_++; + if (me != 2) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("handle_output should be #2; it's %d\n"), + me)); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_output\n"))); + +#if defined (__OpenBSD__) || defined (ACE_VXWORKS) || defined (__Lynx__) + // All that we need written has been written, so don't + // call handle_output again. + this->reactor ()->mask_ops (this->pipe_.read_handle (), + ACE_Event_Handler::WRITE_MASK, + ACE_Reactor::CLR_MASK); +#endif /* __OpenBSD__ || ACE_VXWORKS || __Lynx__ */ + + return 0; +} + +int +Handler::handle_input (ACE_HANDLE fd) +{ + int me = this->dispatch_order_++; + if (me != 3) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("handle_timeout should be #3; it's %d\n"), + me)); + + char buffer[BUFSIZ]; + ssize_t result = ACE::recv (fd, buffer, sizeof buffer); + if (result != ssize_t (ACE_OS::strlen (message))) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler recv'd %b bytes; expected %B\n"), + result, ACE_OS::strlen (message))); + buffer[result] = '\0'; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_input: %C\n"), buffer)); + + if (ACE_OS::strcmp (buffer, message) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Handler text mismatch; received \"%C\"; ") + ACE_TEXT ("expected \"%C\"\n"), + buffer, message)); + + this->reactor ()->end_reactor_event_loop (); + + return 0; +} + +static void +test_reactor_dispatch_order (ACE_Reactor &reactor) +{ + Handler handler (reactor); + if (!handler.ok_) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Error initializing test; abort.\n"))); + return; + } + + bool ok_to_go = true; + + // This should trigger a call to <handle_input>. + ssize_t result = + ACE::send_n (handler.pipe_.write_handle (), + message, + ACE_OS::strlen (message)); + if (result != ssize_t (ACE_OS::strlen (message))) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler sent %b bytes; should be %B\n"), + result, ACE_OS::strlen (message))); + ok_to_go = false; + } + + // This should trigger a call to <handle_timeout>. + if (-1 == reactor.schedule_timer (&handler, + 0, + ACE_Time_Value (0))) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("schedule_timer"))); + ok_to_go = false; + } + + if (ok_to_go) + reactor.run_reactor_event_loop (); + + if (0 != reactor.remove_handler (handler.pipe_.read_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("remover_handler pipe"))); +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Dispatch_Order_Test")); + + ACE_Select_Reactor select_reactor_impl; + ACE_Reactor select_reactor (&select_reactor_impl); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing ACE_Select_Reactor\n"))); + test_reactor_dispatch_order (select_reactor); + + // Winsock 2 things are needed for WFMO_Reactor. +#if defined (ACE_WIN32) && \ + (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0)) + + ACE_WFMO_Reactor wfmo_reactor_impl; + ACE_Reactor wfmo_reactor (&wfmo_reactor_impl); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing ACE_WFMO_Reactor\n"))); + test_reactor_dispatch_order (wfmo_reactor); + +#endif /* ACE_WIN32 && ACE_HAS_WINSOCK2 */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Reactor_Exceptions_Test.cpp b/ACE/tests/Reactor_Exceptions_Test.cpp new file mode 100644 index 00000000000..eda68c3630e --- /dev/null +++ b/ACE/tests/Reactor_Exceptions_Test.cpp @@ -0,0 +1,198 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Exceptions_Test.cpp +// +// = DESCRIPTION +// This is a test that makes sure the <ACE_Reactor> works +// correctly in the face of C++ exceptions and threads. +// +// = AUTHOR +// Luca Priorelli <lucapri@mbox.vol.it> and Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/SOCK_Dgram.h" +#include "ace/INET_Addr.h" +#include "ace/Thread_Manager.h" +#include "ace/Select_Reactor.h" + +ACE_RCSID(tests, Reactor_Exceptions_Test, "$Id$") + +#if defined (ACE_HAS_EXCEPTIONS) + +// Just need a simple exception class. +class Except {}; + +static void +throw_exception (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) throw exception\n"))); + throw Except (); +} + +class My_Handler : public ACE_Event_Handler, public ACE_SOCK_Dgram +{ +public: + My_Handler (const ACE_INET_Addr &local_addr); + + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); +}; + +My_Handler::My_Handler (const ACE_INET_Addr &local_addr) + : ACE_SOCK_Dgram (local_addr) +{ +} + +ACE_HANDLE +My_Handler::get_handle (void) const +{ + return ACE_SOCK_Dgram::get_handle (); +} + +int +My_Handler::handle_input (ACE_HANDLE) +{ + ACE_TCHAR buf[BUFSIZ]; + ACE_INET_Addr from_addr; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Activity occurred on handle %d!\n"), + ACE_SOCK_Dgram::get_handle ())); + + ssize_t n = ACE_SOCK_Dgram::recv (buf, + sizeof buf, + from_addr); + if (n == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("handle_input"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("got buf = %s\n"), + buf)); + + throw_exception (); + return 0; +} + +class My_Reactor : public ACE_Select_Reactor +{ +public: + virtual int handle_events (ACE_Time_Value *max_wait_time) + { + int ret = 0; + + try + { + ret = ACE_Select_Reactor::handle_events (max_wait_time); + } + catch (...) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) catch exception\n"))); + ret = -1; + // do your thing, etc. + } + return ret; + } + + virtual int handle_events (ACE_Time_Value &max_wait_time) + { + return this->handle_events (&max_wait_time); + } +}; + +#if defined (ACE_HAS_THREADS) +static int +worker (void) +{ + ACE_Reactor::instance ()->owner (ACE_OS::thr_self ()); + + for (;;) + if (ACE_Reactor::instance ()->handle_events () == -1) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) exception return\n"))); + break; + } + + return 0; +} +#endif /* ACE_HAS_THREADS */ + +#endif /* ACE_HAS_EXCEPTIONS */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Exceptions_Test")); + +#if defined (ACE_HAS_EXCEPTIONS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Starting tracing\n"))); + + u_short port = argc > 1 ? ACE_OS::atoi (argv[1]) : ACE_DEFAULT_SERVER_PORT; + + ACE_INET_Addr local_addr (port); + ACE_INET_Addr remote_addr (port, ACE_LOCALHOST, PF_INET); + ACE_Reactor reactor (new My_Reactor, true); + + ACE_Reactor::instance (&reactor); + ACE_Thread_Manager *thr_mgr = ACE_Thread_Manager::instance (); + + { + // Make sure handler gets cleaned up before reactor by putting it in its + // own scope + My_Handler handler (local_addr); + + + if (ACE_Reactor::instance ()->register_handler + (&handler, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("register_handler")), + -1); + +#if defined (ACE_HAS_THREADS) + thr_mgr->spawn (ACE_THR_FUNC (worker)); +#else + // Need to figure out how to implement this test. + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_SOCK_Dgram dgram (ACE_sap_any_cast (ACE_INET_Addr &), PF_INET); + + for (size_t i = 0; i < ACE_MAX_ITERATIONS; i++) + dgram.send (ACE_TEXT ("Hello"), + sizeof (ACE_TEXT ("Hello")), + remote_addr); + // Barrier to wait for the other thread to return. + thr_mgr->wait (); + + handler.close (); + dgram.close (); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) exiting main\n"))); +#else + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("C++ exception support not enabled on this platform\n"))); +#endif /* ACE_HAS_EXCEPTIONS */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Reactor_Notification_Queue_Test.cpp b/ACE/tests/Reactor_Notification_Queue_Test.cpp new file mode 100644 index 00000000000..a29ab26769c --- /dev/null +++ b/ACE/tests/Reactor_Notification_Queue_Test.cpp @@ -0,0 +1,223 @@ +/** + * @file Reactor_Notification_Queue_Test.cpp + * + * $Id$ + * + * Verify that the notification queue can be used with large numbers + * of events. + * + * Normally the ACE_Reactor uses a pipe to implement the notify() + * methods. ACE can be compiled with + * ACE_HAS_REACTOR_NOTIFICATION_QUEUE, with this configuration flag + * the Reactor uses a user-space queue to contain the notifications. + * A single message is sent through the pipe to indicate "pipe not + * empty." + * + * @author Carlos O'Ryan <coryan@atdesk.com> + * + */ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/WFMO_Reactor.h" + +ACE_RCSID(tests, + Reactor_Notification_Queue_Test, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + Event_Handler(ACE_Reactor * reactor, + int max_notifications, + char const *test_name); + + /// Run the test + void run(void); + + /// Receive the notifications. + virtual int handle_exception(ACE_HANDLE); + +private: + /** + * @brief Implement a single iteration. + * + * Each iteration of the test consists of sending multiple + * notifications simultaneously. + */ + void send_notifications (void); + + /** + * @brief Return true if the test is finished. + */ + bool done (void) const; + +private: + /** + * @brief The maximum number of notifications in any single + * iteration. + */ + int max_notifications_; + + /** + * @brief The name of the test + */ + char const * test_name_; + /** + * @brief Number of notifications received + */ + int notifications_sent_; + /** + * @brief Number of notifications sent + */ + int notifications_recv_; + + /** + * @brief Number of notifications sent on each iteration + */ + int notifications_curr_; +}; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Notification_Queue_Test")); + +#if !defined(ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Notification queue disabled, ") + ACE_TEXT ("small test version, ") + ACE_TEXT ("which is of no practical use\n"))); + + int max_notifications = 16; +#else + int max_notifications = 1024 * 1024; +#endif /* ACE_HAS_THREADS */ + + { + ACE_Reactor select_reactor ( + new ACE_Select_Reactor, + 1); + + Event_Handler handler(&select_reactor, + max_notifications, + "Select_Reactor"); + + handler.run (); + } + + { + ACE_Reactor tp_reactor (new ACE_TP_Reactor, + 1); + Event_Handler handler(&tp_reactor, + max_notifications, + "TP_Reactor"); + handler.run(); + } + +#if 0 + /// @@todo: Need to talk to Irfan to see how the WFMO handles this. +#if defined (ACE_WIN32) + { + ACE_Reactor wfmo_reactor (new ACE_WFMO_Reactor, + 1); + + Event_Handler handler(&wfmo_reactor, + max_notifications, + "WFMO_Reactor"); + handler.run(); + } +#endif /*ACE_WIN32*/ +#endif /*if 0 */ + ACE_END_TEST; + return 0; +} + +Event_Handler::Event_Handler ( + ACE_Reactor * reactor, + int max_notifications, + char const * test_name) + : ACE_Event_Handler(reactor) + , max_notifications_(max_notifications) + , test_name_(test_name) + , notifications_sent_(0) + , notifications_recv_(0) + , notifications_curr_(1) +{ +} + +void +Event_Handler::run (void) +{ + send_notifications (); + + // Run for 30 seconds or until the test is done. + for(int i = 0; i != 30 && !done(); ++i) + { + ACE_Time_Value tv (1,0); + reactor ()->run_reactor_event_loop(tv); + } + + if(!done()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test %C failed due to timeout ") + ACE_TEXT (" sent=%d,recv=%d \n"), + test_name_, + notifications_sent_, + notifications_recv_)); + } + else + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Test %C passed sent=%d, recv=%d\n"), + test_name_, + notifications_sent_, + notifications_recv_)); + } +} + +int +Event_Handler::handle_exception (ACE_HANDLE) +{ + ++notifications_recv_; + if(notifications_recv_ == notifications_sent_) + { + if(notifications_curr_ >= max_notifications_) + { + + return 0; + } + send_notifications(); + } + return 0; +} + +void +Event_Handler::send_notifications (void) +{ + for (int i = 0; i != notifications_curr_; ++i) + { + if(reactor()->notify (this) == -1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT ("Cannot send notifications in %C test (%d/%d)\n"), + test_name_, i, notifications_curr_)); + return; + } + + ++notifications_sent_; + } + // ACE_ERROR((LM_ERROR, + // "Started iteration with %d notify() calls in test %C\n", + // notifications_curr_, test_name_)); + notifications_curr_ *= 2; +} + +bool +Event_Handler::done (void) const +{ + return (notifications_curr_ >= max_notifications_) + && (notifications_sent_ == notifications_recv_); +} diff --git a/ACE/tests/Reactor_Notify_Test.cpp b/ACE/tests/Reactor_Notify_Test.cpp new file mode 100644 index 00000000000..15adcd8eab6 --- /dev/null +++ b/ACE/tests/Reactor_Notify_Test.cpp @@ -0,0 +1,581 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Notify_Test.cpp +// +// = DESCRIPTION +// This is a test that illustrates how the <ACE_Reactor>'s +// <notify> method works under various <max_notify_iterations> +// settings. It also tests that the <disable_notify_pipe> option +// works correctly. Moreover, if the $ACE_ROOT/ace/config.h file +// has the ACE_HAS_REACTOR_NOTIFICATION_QUEUE option enabled this +// test will also exercise this feature. +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Synch_Traits.h" +#include "ace/Task.h" +#include "ace/Pipe.h" +#include "ace/Auto_Ptr.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/Thread_Semaphore.h" + +ACE_RCSID(tests, Reactor_Notify_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +static const time_t LONG_TIMEOUT = 10; +static const time_t SHORT_TIMEOUT = 2; + +// A class to run a quiet event loop in one thread, and a plain notify() +// from the main thread to make sure a simple notify will wake up a quiet +// reactor. +class Quiet_Notify_Tester : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + Quiet_Notify_Tester (void) : result_ (0) {} + ~Quiet_Notify_Tester (void) { this->wait (); } + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0); + // Start the reactor event thread. + + // Run the reactor event loop. + virtual int svc (void); + + // Return the test result, 0 ok, -1 fail + int result (void) const { return this->result_; } + +private: + ACE_Reactor r_; + int result_; +}; + +int +Quiet_Notify_Tester::open (void *) +{ + this->reactor (&this->r_); + return this->activate (); +} + +int +Quiet_Notify_Tester::svc (void) +{ + // Count on the main thread doing a notify in less than LONG_TIMEOUT + // seconds. If we don't get it, report a failure. + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Starting quiet event loop\n"))); + this->r_.owner (ACE_Thread::self ()); + ACE_Time_Value tmo (LONG_TIMEOUT); + int status = this->r_.handle_events (&tmo); + time_t remain = tmo.sec (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) event loop status %d, %: secs remain\n"), + status, remain)); + if (remain == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Notify did not wake quiet event loop\n"))); + this->result_ = -1; + } + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Notify woke quiet event loop\n"))); + } + return 0; +} + +static int +run_quiet_notify_test (void) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) Starting quiet notify test\n")); + Quiet_Notify_Tester t; + if (t.open () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Quiet notify activate")), + -1); + // Now sleep a bit, then do a simple, reactor wake-up + ACE_OS::sleep (SHORT_TIMEOUT); + t.reactor ()->notify (); + t.wait (); + ACE_DEBUG ((LM_DEBUG, "(%t) Quiet notify test done\n")); + return t.result (); +} + + +class Supplier_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Supplier_Task (int disable_notify_pipe, + const ACE_Time_Value &tv); + // Constructor. + + ~Supplier_Task (void); + // Destructor. + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0); + // Make this an Active Object. + + virtual int close (u_long); + // Close down the supplier. + //FUZZ: enable check_for_lack_ACE_OS + + virtual int svc (void); + // Generates events and sends them to the <Reactor>'s <notify> + // method. + + virtual int handle_exception (ACE_HANDLE); + // Releases the <waiter_> semaphore when called by the <Reactor>'s + // notify handler. + + virtual int handle_output (ACE_HANDLE); + // Called every time through the main <ACE_Reactor> event loop to + // illustrate the difference between "limited" and "unlimited" + // notification. + + void release (void); + // Release the <waiter_>. + +private: + int perform_notifications (int notifications); + // Perform the notifications. + + ACE_Thread_Semaphore waiter_; + // Used to hand-shake between the <Supplier_Task> and the + // <Reactor>'s notify mechanism. + + ACE_Pipe pipe_; + // We use this pipe just to get a handle that is always "active," + // i.e., the <ACE_Reactor> will always dispatch its <handle_output> + // method. + + int disable_notify_pipe_; + // Keeps track of whether the notification pipe in the <ACE_Reactor> + // has been diabled or not. + + int long_timeout_; + // Keeps track of whether we're running with a <LONG_TIMEOUT>, which + // is used for the ACE_HAS_REACTOR_NOTIFICATION_QUEUE portion of + // this test. +}; + +void +Supplier_Task::release (void) +{ + this->waiter_.release (); +} + +Supplier_Task::Supplier_Task (int disable_notify_pipe, + const ACE_Time_Value &tv) + : waiter_ ((unsigned int) 0), // Make semaphore "locked" by default. + disable_notify_pipe_ (disable_notify_pipe), + long_timeout_ (tv.sec () == LONG_TIMEOUT) +{ +} + +int +Supplier_Task::open (void *) +{ + // Create the pipe. + int result; + + result = this->pipe_.open (); + ACE_ASSERT (result != -1); + + // Register the pipe's write handle with the <Reactor> for writing. + // This should mean that it's always "active." + if (long_timeout_ == 0) + { + result = ACE_Reactor::instance ()->register_handler + (this->pipe_.write_handle (), + this, + ACE_Event_Handler::WRITE_MASK); + ACE_ASSERT (result != -1); + } + + // Make this an Active Object. + result = this->activate (THR_BOUND | THR_DETACHED); + ACE_ASSERT (result != -1); + return 0; +} + +int +Supplier_Task::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Supplier_Task::close\n"))); + + int result; + + if (long_timeout_ == 0) + { + result = ACE_Reactor::instance ()->remove_handler + (this->pipe_.write_handle (), + ACE_Event_Handler::WRITE_MASK); + ACE_ASSERT (result != -1); + } + else + { + // Wait to be told to shutdown by the main thread. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) waiting to be shutdown by main thread\n"))); + result = this->waiter_.acquire (); + ACE_ASSERT (result != -1); + } + return 0; +} + +Supplier_Task::~Supplier_Task (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) ~Supplier_Task\n"))); + this->pipe_.close (); +} + +int +Supplier_Task::perform_notifications (int notifications) +{ + ACE_Reactor::instance ()->max_notify_iterations (notifications); + + size_t iterations = ACE_MAX_ITERATIONS; + + if (this->long_timeout_) + { + iterations *= (iterations * iterations * 2); +#if defined (ACE_VXWORKS) + // scale down otherwise the test won'y finish in time + iterations /= 4; +#endif + } + + for (size_t i = 0; i < iterations; i++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) notifying reactor on iteration %d\n"), + i)); + + int result; + + // Notify the Reactor, which will call <handle_exception>. + result = ACE_Reactor::instance ()->notify (this); + if (result == -1) + { + if (errno == ETIME) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("notify"))); + else + ACE_ASSERT (result != -1); + } + + // Wait for our <handle_exception> method to release the + // semaphore. + if (this->long_timeout_ == 0 + && this->disable_notify_pipe_ == 0) + { + result = this->waiter_.acquire (); + ACE_ASSERT (result != -1); + } + } + + return 0; +} + +int +Supplier_Task::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** starting unlimited notifications test\n"))); + + // Allow an unlimited number of iterations per + // <ACE_Reactor::notify>. + this->perform_notifications (-1); + + if (this->long_timeout_ == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** starting limited notifications test\n"))); + + // Only allow 1 iteration per <ACE_Reactor::notify> + this->perform_notifications (1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) **** exiting thread test\n"))); + } + return 0; +} + +int +Supplier_Task::handle_exception (ACE_HANDLE handle) +{ + ACE_ASSERT (handle == ACE_INVALID_HANDLE); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) handle_exception\n"))); + + this->waiter_.release (); + return 0; +} + +int +Supplier_Task::handle_output (ACE_HANDLE handle) +{ + ACE_ASSERT (handle == this->pipe_.write_handle ()); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) handle_output\n"))); + + // This function is called by the main thread, believe it or not :-) + // That's because the pipe's write handle is always active. Thus, + // we can give the <Supplier_Task> a chance to run in its own + // thread. + ACE_OS::thr_yield (); + + return 0; +} + +static int +run_test (int disable_notify_pipe, + const ACE_Time_Value &tv) +{ + // Create special reactors with the appropriate flags enabled. + + ACE_Select_Reactor *reactor_impl = 0; + if (disable_notify_pipe) + ACE_NEW_RETURN (reactor_impl, + ACE_Select_Reactor (0, 0, 1), + -1); + else + ACE_NEW_RETURN (reactor_impl, + ACE_Select_Reactor, + -1); + + ACE_Reactor *reactor; + ACE_NEW_RETURN (reactor, + ACE_Reactor (reactor_impl, 1), // Delete implementation + -1); + + // Make sure this stuff gets cleaned up when this function exits. + auto_ptr<ACE_Reactor> r (reactor); + + // Set the Singleton Reactor. + ACE_Reactor *orig_reactor = ACE_Reactor::instance (reactor); + ACE_ASSERT (ACE_LOG_MSG->op_status () != -1); + ACE_ASSERT (ACE_Reactor::instance () == reactor); + + Supplier_Task task (disable_notify_pipe, + tv); + ACE_ASSERT (ACE_LOG_MSG->op_status () != -1); + + int result; + + result = task.open (); + ACE_ASSERT (result != -1); + + if (tv.sec () == LONG_TIMEOUT) + // Sleep for a while so that the <ACE_Reactor>'s notification + // buffers will fill up! + ACE_OS::sleep (tv); + + int shutdown = 0; + + // Run the event loop that handles the <handle_output> and + // <handle_exception> notifications. + for (int iteration = 1; + shutdown == 0; + iteration++) + { + ACE_Time_Value timeout (tv); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) starting handle_events() on iteration %d") + ACE_TEXT (" with time-out = %d seconds\n"), + iteration, + timeout.sec ())); + + // Use a timeout to inform the Reactor when to shutdown. + switch (ACE_Reactor::instance ()->handle_events (timeout)) + { + case -1: + if (! disable_notify_pipe) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("reactor"))); + shutdown = 1; + break; + /* NOTREACHED */ + case 0: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) handle_events timed out\n"))); + shutdown = 1; + break; + /* NOTREACHED */ + default: + break; + /* NOTREACHED */ + } + } + + if (tv.sec () == LONG_TIMEOUT) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) releasing supplier task thread\n"))); + task.release (); + } + ACE_Reactor::instance (orig_reactor); + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +class Purged_Notify : public ACE_Event_Handler +{ + // = TITLE + // <run_notify_purge_test> tests the reactor's + // purge_pending_notifications function. It does 2 notifications, + // and explicitly cancels one, and deletes the other's event + // handler, which should cause it to be cancelled as well. + + virtual int handle_exception (ACE_HANDLE = ACE_INVALID_HANDLE) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Got a notify that should have been purged!\n")), + 0); + } +}; + +static int +run_notify_purge_test (void) +{ + int status; + ACE_Reactor *r = ACE_Reactor::instance (); + { + Purged_Notify n1; + Purged_Notify *n2; + + ACE_NEW_RETURN (n2, Purged_Notify, -1); + auto_ptr<Purged_Notify> ap (n2); + + // First test: + // Notify EXCEPT, and purge ALL + r->notify (&n1); // the mask is EXCEPT_MASK + + status = r->purge_pending_notifications (&n1); + if (status == -1 && errno == ENOTSUP) + return 0; // Select Reactor w/o ACE_HAS_REACTOR_NOTIFICATION_QUEUE + if (status != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Purged %d notifies; expected 1\n"), + status)); + // Second test: + // Notify READ twice, and WRITE once, and purge READ and WRITE - should purge 3 times. + r->notify (&n1, ACE_Event_Handler::READ_MASK); + r->notify (&n1, ACE_Event_Handler::READ_MASK); + r->notify (&n1, ACE_Event_Handler::WRITE_MASK); + status = r->purge_pending_notifications + (&n1, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK); + if (status != 3) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Purged %d notifies; expected 3\n"), + status)); + // Third test: + // Notify READ on 2 handlers, and purge READ|WRITE on all handlers. Should purge 2 + r->notify (&n1, ACE_Event_Handler::READ_MASK); + r->notify (n2, ACE_Event_Handler::READ_MASK); + status = r->purge_pending_notifications + (0, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK); + if (status != 2) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Purged %d notifies; expected 2\n"), + status)); + // Forth test: + // Notify EXCEPT and WRITE, purge READ. Should not purge + r->notify (&n1); // the mask is EXCEPT_MASK + r->notify (&n1, ACE_Event_Handler::WRITE_MASK); + status = r->purge_pending_notifications + (&n1, ACE_Event_Handler::READ_MASK); + if (status != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Purged %d notifies; expected 0\n"), + status)); + // Fifth test: + r->notify (n2); + + // The destructor of the event handler no longer removes the + // notifications. It is the application's responsability to do + // so. + r->purge_pending_notifications(n2, + ACE_Event_Handler::ALL_EVENTS_MASK); + r->purge_pending_notifications(&n1, + ACE_Event_Handler::ALL_EVENTS_MASK); + } + + ACE_Time_Value t (1); + status = r->handle_events (t); // Should be nothing to do, and time out + return status < 0 ? 1 : 0; // Return 0 for all ok, else error +} + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Notify_Test")); + + int test_result = 0; // Innocent until proven guilty + + if (0 == run_notify_purge_test ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("purge_pending_notifications test OK\n"))); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("purge_pending_notifications test FAIL\n"))); + test_result = 1; + } + +#if defined (ACE_HAS_THREADS) + if (0 != run_quiet_notify_test ()) + test_result = 1; + + ACE_Time_Value timeout (SHORT_TIMEOUT); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) running tests with notify pipe enabled") + ACE_TEXT (" and time-out = %d seconds\n"), + timeout.sec ())); + run_test (0, timeout); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) running tests with notify pipe disabled") + ACE_TEXT (" and time-out = %d seconds\n"), + timeout.sec ())); + run_test (1, timeout); + + timeout.set (LONG_TIMEOUT, 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) running tests with reactor notification ") + ACE_TEXT ("pipe enabled\n") + ACE_TEXT (" and time-out = %d seconds\n"), + timeout.sec ())); + run_test (0, timeout); + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return test_result; +} diff --git a/ACE/tests/Reactor_Performance_Test.cpp b/ACE/tests/Reactor_Performance_Test.cpp new file mode 100644 index 00000000000..d20b4199528 --- /dev/null +++ b/ACE/tests/Reactor_Performance_Test.cpp @@ -0,0 +1,420 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Performance_Test.cpp +// +// = DESCRIPTION +// This test is used to time the dispatching mechanisms of the +// <ACE_Reactor>s. Both the <ACE_WFMO_Reactor> and +// <ACE_Select_Reactor> can be tested. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "Reactor_Performance_Test.h" +#include "ace/Profile_Timer.h" +#include "ace/Get_Opt.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Acceptor.h" +#include "ace/Connector.h" +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/Auto_Ptr.h" + +ACE_RCSID(tests, Reactor_Performance_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// Number of client (user) threads +static int opt_nconnections = 5; + +// Number of data exchanges +static int opt_nloops = 200; + +// Use the WFMO_Reactor +static int opt_wfmo_reactor = 0; + +// Use the Select_Reactor +static int opt_select_reactor = 0; + +// Extra debug messages +static int opt_debug = 0; + +int Read_Handler::waiting_ = 0; + +void +Read_Handler::set_countdown (int nconnections) +{ + Read_Handler::waiting_ = nconnections; +} + +// Initialize the Svc_Handler +int +Read_Handler::open (void *) +{ + if (this->peer ().enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Read_Handler::open, cannot set non blocking mode\n")), + -1); + + if (reactor ()->register_handler (this, READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Read_Handler::open, cannot register handler\n")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) created svc_handler for handle %d\n"), + get_handle ())); + return 0; +} + +// Handle incoming data +int +Read_Handler::handle_input (ACE_HANDLE handle) +{ + ACE_UNUSED_ARG (handle); + char buf[BUFSIZ]; + + while (1) + { + ssize_t result = this->peer ().recv (buf, sizeof (buf) - 1); + + if (result > 0) + { + if (opt_debug) + { + buf[result] = 0; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Read_Handler::handle_input: %s\n"), + buf)); + } + } + else if (result < 0) + { + if (errno == EWOULDBLOCK) + return 0; + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("handle_input: %p (errno: %d)\n"), + ACE_TEXT ("recv"), errno)); + + // This will cause handle_close to get called. + return -1; + } + } + else // result == 0 + { + // This will cause handle_close to get called. + return -1; + } + } + + ACE_NOTREACHED (return 0); +} + +// Handle connection shutdown. + +int +Read_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask) +{ + ACE_UNUSED_ARG (handle); + ACE_UNUSED_ARG (close_mask); + + // Reduce count. + waiting_--; + + // If no connections are open. + if (waiting_ == 0) + ACE_Reactor::instance ()->end_reactor_event_loop (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Read_Handler::handle_close closing down\n"))); + + // Shutdown + this->destroy (); + return 0; +} + +int +Write_Handler::open (void *) +{ + return 0; +} + +int +Write_Handler::send_data (void) +{ + int send_size = sizeof (ACE_ALPHABET) - 1; + + if (this->peer ().send_n (ACE_ALPHABET, + send_size) != send_size) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("send_n")), + -1); + return 0; +} + +// Connection factories +typedef ACE_Connector<Write_Handler, ACE_SOCK_CONNECTOR> CONNECTOR; +typedef ACE_Acceptor<Read_Handler, ACE_SOCK_ACCEPTOR> ACCEPTOR; + +// Execute the client tests. +void * +client (void *arg) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) running client\n"))); + + ACE_INET_Addr *connection_addr = + reinterpret_cast<ACE_INET_Addr *> (arg); + CONNECTOR connector; + + int i; + + // Automagic memory cleanup. + Write_Handler **temp_writers = 0; + ACE_NEW_RETURN (temp_writers, + Write_Handler *[opt_nconnections], + 0); + ACE_Auto_Basic_Array_Ptr <Write_Handler *> writers (temp_writers); + + ACE_TCHAR *temp_failed = 0; + ACE_NEW_RETURN (temp_failed, + ACE_TCHAR[opt_nconnections], + 0); + ACE_Auto_Basic_Array_Ptr <ACE_TCHAR> failed_svc_handlers (temp_failed); + + // Automagic memory cleanup. + ACE_INET_Addr *temp_addresses; + ACE_NEW_RETURN (temp_addresses, + ACE_INET_Addr [opt_nconnections], + 0); + ACE_Auto_Array_Ptr <ACE_INET_Addr> addresses (temp_addresses); + + // Initialize array. + for (i = 0; i < opt_nconnections; i++) + { + writers[i] = 0; + addresses[i] = *connection_addr; + } + + // Connection all <opt_nconnections> svc_handlers + int result = connector.connect_n (opt_nconnections, + writers.get (), + addresses.get (), + failed_svc_handlers.get ()); + if (result == -1) + { + // Print out the connections that failed... + for (i = 0; i < opt_nconnections; i++) + if (failed_svc_handlers.get ()[i]) + { + ACE_INET_Addr failed_addr = addresses.get()[i]; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) connection failed to %s, %d\n"), + failed_addr.get_host_name (), + failed_addr.get_port_number ())); + } + return 0; + } + + // If no connections failed (result == 0) then there should be valid + // ACE_Svc_handler pointers in each writers[] position. Iterate to + // send data + for (int j = 0; j < opt_nloops; j++) + for (i = 0; i < opt_nconnections; i++) + if (writers[i]->send_data () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("writer::send_data")), + 0); + // Cleanup + for (i = 0; i < opt_nconnections; i++) + writers[i]->destroy (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) finishing client\n"))); + return 0; +} + +// Sets up the correct reactor (based on platform and options). + +void +create_reactor (void) +{ + ACE_Reactor_Impl *impl = 0; + + if (opt_wfmo_reactor) + { +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + ACE_NEW (impl, + ACE_WFMO_Reactor); +#endif /* ACE_WIN32 */ + } + else if (opt_select_reactor) + ACE_NEW (impl, + ACE_Select_Reactor); + + ACE_Reactor *reactor = 0; + ACE_NEW (reactor, + ACE_Reactor (impl)); + ACE_Reactor::instance (reactor); +} + +// Print stats. + +void +print_results (ACE_Profile_Timer::ACE_Elapsed_Time &et) +{ + const ACE_TCHAR *reactor_type = 0; + + if (opt_wfmo_reactor) + reactor_type = ACE_TEXT ("WFMO_Reactor"); + else if (opt_select_reactor) + reactor_type = ACE_TEXT ("Select_Reactor"); + else + reactor_type = ACE_TEXT ("Platform's default Reactor"); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\tReactor_Performance Test statistics:\n\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tReactor Type: %s\n"), + reactor_type)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tConnections: %d\n"), + opt_nconnections)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\tIteration per connection: %d\n"), + opt_nloops)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n\tTiming results:\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\t\treal time = %f secs \n\t\tuser time = %f secs \n\t\tsystem time = %f secs\n\n"), + et.real_time, + et.user_time, + et.system_time)); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Performance_Test")); + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("dswc:l:"), 1); + for (int c; (c = getopt ()) != -1; ) + //FUZZ: enble check_for_lack_ACE_OS + switch (c) + { + case 's': + opt_select_reactor = 1; + break; + case 'w': + opt_wfmo_reactor = 1; + break; + case 'c': + opt_nconnections = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'l': + opt_nloops = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'd': + opt_debug = 1; + break; + } + + // Sets up the correct reactor (based on platform and options). + create_reactor (); + + // Manage memory automagically. + auto_ptr<ACE_Reactor> reactor (ACE_Reactor::instance ()); + auto_ptr<ACE_Reactor_Impl> impl; + + // If we are using other that the default implementation, we must + // clean up. + if (opt_select_reactor || opt_wfmo_reactor) + { + auto_ptr<ACE_Reactor_Impl> auto_impl (ACE_Reactor::instance ()->implementation ()); + impl = auto_impl; + } + + Read_Handler::set_countdown (opt_nconnections); + + // Acceptor + ACCEPTOR acceptor; + ACE_INET_Addr server_addr; + + // Bind acceptor to any port and then find out what the port was. + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1 + || acceptor.acceptor ().get_local_addr (server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("open")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) starting server at port %d\n"), + server_addr.get_port_number ())); + + ACE_INET_Addr connection_addr (server_addr.get_port_number (), + ACE_DEFAULT_SERVER_HOST); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + (void *) &connection_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("thread create failed"))); + + ACE_Time_Value run_limit (opt_nloops / 10); + + ACE_Profile_Timer timer; + timer.start (); + const int status = + ACE_Reactor::instance ()->run_reactor_event_loop (run_limit); + timer.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + timer.elapsed_time (et); + + // Print results + print_results (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) waiting for the client thread...\n"))); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_END_TEST; + return status; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Performance_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/Reactor_Performance_Test.h b/ACE/tests/Reactor_Performance_Test.h new file mode 100644 index 00000000000..bde943c2951 --- /dev/null +++ b/ACE/tests/Reactor_Performance_Test.h @@ -0,0 +1,62 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Performance_Test.h +// +// = DESCRIPTION +// This file includes template class definitions for the +// Reactor_Performance_Test.cpp program. This class gets its own +// header file to work around AIX C++ compiler "features" related +// to template instantiation... +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> and +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_REACTOR_PERFORMANCE_TEST_H +#define ACE_TESTS_REACTOR_PERFORMANCE_TEST_H + +#include "ace/SOCK_Stream.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Svc_Handler.h" + +class Read_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_SYNCH> +{ + // = TITLE + // Simple class for reading in the data +public: + static void set_countdown (int nconnections); + + virtual int open (void *); + virtual int handle_input (ACE_HANDLE h); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + // The Svc_Handler callbacks. + +private: + static int waiting_; + // How many connections are we waiting for. +}; + +class Write_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_SYNCH> +{ + // = TITLE + // This Svc_Handler simply connects to a server and sends some + // output to it. Its purpose is to feed the test. +public: + virtual int open (void *); + virtual int send_data (void); +}; + +#endif /* ACE_TESTS_REACTOR_PERFORMANCE_TEST_H */ diff --git a/ACE/tests/Reactor_Registration_Test.cpp b/ACE/tests/Reactor_Registration_Test.cpp new file mode 100644 index 00000000000..2e3aaf08cb9 --- /dev/null +++ b/ACE/tests/Reactor_Registration_Test.cpp @@ -0,0 +1,203 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Registration_Test.cpp +// +// = DESCRIPTION +// This is a test of registering handlers with the Reactor. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Pipe.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/ACE.h" + +ACE_RCSID(tests, Reactor_Registration_Test, "$Id$") + +static const char message[] = "abcdefghijklmnopqrstuvwxyz"; +static const size_t message_size = 26; +static int iteration = 1; + +class Event_Handler : public ACE_Event_Handler +{ +public: + + Event_Handler (ACE_Reactor &reactor, + ACE_HANDLE read, + ACE_HANDLE write); + + ~Event_Handler (void); + + int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE); + + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + ACE_Pipe pipe_; + bool ok_; +}; + +Event_Handler::Event_Handler (ACE_Reactor &reactor, + ACE_HANDLE read, + ACE_HANDLE write) + : ACE_Event_Handler (&reactor), + pipe_ (read, write), + ok_ (false) +{ + if (read == ACE_INVALID_HANDLE) + { + if (0 != this->pipe_.open ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Event_Handler pipe"))); + return; + } + } + + if (0 != this->reactor ()->register_handler (this->pipe_.read_handle (), + this, + ACE_Event_Handler::READ_MASK)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Event_Handler register_handler"))); + return; + } + + ssize_t result = ACE::send_n (this->pipe_.write_handle (), + message, + message_size); + if (result != ssize_t (message_size)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Event_Handler sent %b bytes; should be %B\n"), + result, message_size)); + if (result <= 0) + return; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event_Handler::Event_Handler for %@\n"), + this)); + this->ok_ = true; +} + +Event_Handler::~Event_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event_Handler::~Event_Handler for %@\n"), + this)); +} + +int +Event_Handler::handle_input (ACE_HANDLE handle) +{ + char buf[message_size + 1]; + + ssize_t result = + ACE::recv_n (handle, + buf, + sizeof buf - 1); + if (result != static_cast<ssize_t> (message_size)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler recv'd %b bytes; expected %B\n"), + result, message_size)); + buf[result > 0 ? result : 0] = '\0'; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Message %C received for %@\n"), + buf, + this)); + + return -1; +} + +int +Event_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + switch (iteration) + { + case 1: + new Event_Handler (*this->reactor (), + ACE_INVALID_HANDLE, + ACE_INVALID_HANDLE); + break; + case 2: + new Event_Handler (*this->reactor (), + this->pipe_.read_handle (), + this->pipe_.write_handle ()); + break; + case 3: + this->reactor ()->end_reactor_event_loop (); + break; + } + + iteration++; + delete this; + + return 0; +} + +void +test (ACE_Reactor_Impl &reactor_impl, + const char *reactor_type) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nTesting with %C\n\n"), + reactor_type)); + + ACE_Reactor reactor (&reactor_impl, + 0); + + Event_Handler *e = new Event_Handler (reactor, + ACE_INVALID_HANDLE, + ACE_INVALID_HANDLE); + if (!e->ok_) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error initializing test; aborting.\n"))); + delete e; + return; + } + + reactor.run_reactor_event_loop (); +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Registration_Test")); + + iteration = 1; + ACE_Select_Reactor select_reactor; + test (select_reactor, "ACE_Select_Reactor"); + + iteration = 1; + ACE_TP_Reactor tp_reactor; + test (tp_reactor, "ACE_TP_Reactor"); + + // The ACE_WFMO_Reactor stuff needs Winsock2 +#if defined (ACE_WIN32) && \ + (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0)) + iteration = 1; + ACE_WFMO_Reactor wfmo_reactor; + test (wfmo_reactor, "ACE_WFMO_Reactor"); +#endif /* ACE_WIN32 && ACE_HAS_WINSOCK2 */ + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/Reactor_Remove_Resume_Test.cpp b/ACE/tests/Reactor_Remove_Resume_Test.cpp new file mode 100644 index 00000000000..820dfc46e1d --- /dev/null +++ b/ACE/tests/Reactor_Remove_Resume_Test.cpp @@ -0,0 +1,486 @@ +/** + * @file Reactor_Remove_Resume_Test.cpp + * + * $Id$ + * + * This test verifies that ACE reactors only remove or resume the event + * handler used during an upcall, not another with same handle value. + * There is are least one case where the event handler can change + * during an upcall. The event handler could be removed by another + * thread, and a new one is registered for a handle of the same value + * (e.g. closed and then reopened) during an upcall. "Misbehaved" + * event handlers could also cause this problem by closing and + * deregistering the event handler during an upcall. + * + * @author Ossama Othman + */ + + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/Dev_Poll_Reactor.h" +#include "ace/Pipe.h" +#include "ace/Auto_Ptr.h" + +#include <algorithm> +#include <functional> + +int overall_result = 0; + +// ------------------------------------------------------------ + +class Bogus_Handler : public ACE_Event_Handler +{ +public: + + Bogus_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close); + +protected: + + virtual ~Bogus_Handler (void); + + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + virtual int resume_handler (void); + +private: + + ACE_HANDLE const read_handle_; + + // If the reactor closes the event handler before it gets the okay, + // we will issue an error. + bool & okay_to_close_; + +}; + +Bogus_Handler::Bogus_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close) + : ACE_Event_Handler (reactor) + , read_handle_ (read_handle) + , okay_to_close_ (okay_to_close) +{ + this->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); +} + +Bogus_Handler::~Bogus_Handler (void) +{ +} + +ACE_HANDLE +Bogus_Handler::get_handle (void) const +{ + return this->read_handle_; +} + +int +Bogus_Handler::handle_input (ACE_HANDLE) +{ + // This event handler should have been suspended, meaning it should + // not have received any events. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Bogus_Handler received an ") + ACE_TEXT ("unexpected event.\n")), + -1); +} + +int +Bogus_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + if (this->okay_to_close_) + return 0; + + overall_result = -1; + + // This event handler is being closed by the reactor unexpectedly. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Bogus_Handler unexpectedly closed\n ")), + -1); +} + +int +Bogus_Handler::resume_handler (void) +{ + // We don't want the reactor to resume this event handler. + return ACE_APPLICATION_RESUMES_HANDLER; +} + +// ------------------------------------------------------------ + +class Bad_Handler : public ACE_Event_Handler +{ +public: + + Bad_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close, + bool suspension_test); + + ACE_HANDLE write_handle (void) const; + +protected: + + virtual ~Bad_Handler (void); + +private: + + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + int handle_input_result (void) const; + +private: + + ACE_HANDLE const read_handle_; + + bool handle_close_called_; + + // Passed on to the Bogus_Handler constructor. Not used by the + // Bad_Handler, otherwise. + bool & okay_to_close_; + + // Are we running the event handler suspension or removal test? + bool suspension_test_; + +}; + +Bad_Handler::Bad_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close, + bool suspension_test) + : ACE_Event_Handler (reactor) + , read_handle_ (read_handle) + , handle_close_called_ (false) + , okay_to_close_ (okay_to_close) + , suspension_test_ (suspension_test) +{ + this->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + +} + +Bad_Handler::~Bad_Handler (void) +{ +} + +ACE_HANDLE +Bad_Handler::get_handle (void) const +{ + return this->read_handle_; +} + +int +Bad_Handler::handle_input (ACE_HANDLE handle) +{ + // Remove ourselves from the reactor, and trigger a different one + // with the same handle to be registered. + if (this->reactor ()->remove_handler (handle, + ACE_Event_Handler::READ_MASK) != 0) + { + overall_result = -1; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to remove Bad_Handler ") + ACE_TEXT ("from reactor\n")), + 1); + } + + ACE_Event_Handler_var bogus_handler ( + new Bogus_Handler (this->reactor (), + handle, + this->okay_to_close_)); + + // Register and suspend a new handler. + if (this->reactor ()->register_handler (handle, + bogus_handler.handler (), + ACE_Event_Handler::READ_MASK) != 0 + || this->reactor ()->suspend_handler (handle) != 0) + { + overall_result = -1; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to register or suspend ") + ACE_TEXT ("Bogus_Handler\n")), + 1); + } + + // Make sure a read event exists for dispatching during the next + // event loop iteration. + if (this->reactor ()->schedule_wakeup (handle, + ACE_Event_Handler::READ_MASK) == -1) + { + overall_result = -1; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Wakeup scheduling failed\n"))); + } + + return this->handle_input_result (); +} + +int +Bad_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + if (this->handle_close_called_) + { + overall_result = -1; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Bad_Handler::handle_close() called ") + ACE_TEXT ("more than once\n")), + -1); + } + + this->handle_close_called_ = true; + + return 0; +} + +int +Bad_Handler::handle_input_result (void) const +{ + return + (this->suspension_test_ + + // Attempt to force reactor to resume the handle after the + // upcall completes. + ? 0 + + // Attempt to force the new event handler to be removed from the + // reactor. + : -1); +} + +// ------------------------------------------------------------ +int +register_handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close, + bool suspension_test) +{ + ACE_Event_Handler_var bad_handler (new Bad_Handler (reactor, + read_handle, + okay_to_close, + suspension_test)); + + // Register for read events. + if (reactor->register_handler (bad_handler.handler (), + ACE_Event_Handler::READ_MASK) != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to register Bad_Handler with ") + ACE_TEXT ("reactor\n")), + -1); + } + + return 0; +} + +// ------------------------------------------------------------ + +int +send_data (ACE_HANDLE write_handle) +{ + char const foo[] = "foo"; + size_t const len = sizeof (foo); // We want the number of bytes, not + // the number of characters. + + ACE_Time_Value const timeout (2); + + // Trigger a read event on the pipe handle. This shouldn't timeout + // since the pipe should be able to hold such a small amount of + // data. + size_t bytes_transferred = 0; + ssize_t const result = + ACE::send_n (write_handle, + foo, + len, + &timeout, // timeout + &bytes_transferred); + + if (result == -1 + || bytes_transferred != len) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Unable to send data")), + -1); + } + + return 0; +} + +// ------------------------------------------------------------ + +int +handle_events (ACE_Reactor & reactor, + bool & okay_to_close) +{ + ACE_Time_Value timeout (2); + + // Only one event handler should have been dispatched. + if (reactor.handle_events (&timeout) != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Initial event dispatch failed\n"))); + } + else + { + okay_to_close = true; + + // Run the event loop again in an attempt to make the reactor + // dispatch the newly registered event handler. No events + // should be dispatched. + timeout.sec (2); + int const result = reactor.handle_events (&timeout); + + + if (result > 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unexpectedly dispatched an event\n"))); + } + else if (result < 0) + { + overall_result = -1; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Event loop failed unexpectedly\n"))); + } + else + return 0; + } + + return -1; +} + +// ------------------------------------------------------------ + +typedef auto_ptr<ACE_Reactor_Impl> (*reactor_factory_type) (void); + +auto_ptr<ACE_Reactor_Impl> +tp_reactor_factory (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Creating ACE_TP_Reactor.\n"))); + + return auto_ptr<ACE_Reactor_Impl> (new ACE_TP_Reactor); +} + +#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) +auto_ptr<ACE_Reactor_Impl> +dev_poll_reactor_factory (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Creating ACE_Dev_Poll_Reactor.\n"))); + + return auto_ptr<ACE_Reactor_Impl> (new ACE_Dev_Poll_Reactor); +} + +#endif /* ACE_HAS_EVENT_POLL || ACE_HAS_DEV_POLL */ + +// ------------------------------------------------------------ +/** + * @struct Caller + * + * @brief Reactor test execution functor. + * + * Reactor test execution functor. + */ +struct Run_Test : public std::unary_function<reactor_factory_type, void> +{ + /// Function call operator overload. + void operator() (reactor_factory_type factory) + { + bool const suspension_test[] = + { + true, // Run suspension test. + false // Run removal test. + }; + + bool const * const begin = suspension_test; + bool const * const end = + suspension_test + + sizeof (suspension_test) / sizeof (suspension_test[0]); + + for (bool const * i = begin; i != end; ++i) + { + bool const suspension_test = *i; + + if (suspension_test) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("** Running suspension test **\n"))); + } + else + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("** Running removal test **\n"))); + } + + auto_ptr<ACE_Reactor_Impl> the_factory (factory ()); + ACE_Reactor reactor (the_factory.get ()); + + // In this test, it's only okay to close the Bogus_Handler + // when the reactor is destroyed. + bool okay_to_close = false; + + ACE_Pipe the_pipe; + if (the_pipe.open () != 0 + || register_handler (&reactor, + the_pipe.read_handle (), + okay_to_close, + suspension_test) != 0 + || send_data (the_pipe.write_handle ()) != 0 + || handle_events (reactor, okay_to_close) != 0) + { + overall_result = -1; + } + + okay_to_close = true; + + ACE_DEBUG ((LM_INFO, ACE_TEXT ("\n"))); // For log aesthetics. + } + } +}; + +// ------------------------------------------------------------ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Remove_Resume_Test")); + + static reactor_factory_type const factories[] = + { + tp_reactor_factory +#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) + , dev_poll_reactor_factory +#endif /* ACE_HAS_EVENT_POLL || ACE_HAS_DEV_POLL */ + }; + + static size_t const factory_count = sizeof (factories) / sizeof (factories[0]); + + std::for_each (factories, factories + factory_count, Run_Test ()); + + if (overall_result != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test failed.\n"))); + else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Test passed.\n"))); + + ACE_END_TEST; + + return overall_result; +} diff --git a/ACE/tests/Reactor_Timer_Test.cpp b/ACE/tests/Reactor_Timer_Test.cpp new file mode 100644 index 00000000000..cc34c5b1b70 --- /dev/null +++ b/ACE/tests/Reactor_Timer_Test.cpp @@ -0,0 +1,272 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactor_Timer_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the timer mechanism of +// the reactor. Scheduling timers, resetting timer intervals, +// handling expired timers and cancelling scheduled timers are +// all exercised in this test. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Douglas C. Schmidt +// <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Timer_Queue.h" +#include "ace/Reactor.h" +#include "ace/High_Res_Timer.h" +#include "ace/Trace.h" +#include "ace/Recursive_Thread_Mutex.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(tests, Reactor_Timer_Test, "$Id$") + +static int done = 0; +static int the_count = 0; +static int odd = 0; + +class Time_Handler : public ACE_Event_Handler +{ +public: + Time_Handler (); + // Default constructor + + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg); + // Handle the timeout. + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + // Called when <Time_Handler> is removed. + + long timer_id (void) const; + // Return our timer id. + + void timer_id (long); + // Set our timer id; + +private: + long timer_id_; + // Stores the id of this timer. +}; + +Time_Handler::Time_Handler () +: timer_id_ (-1) +{ + // Nothing +} + +int +Time_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("[%x] handle = %d, close_mask = %d, timer id = %d\n"), + this, + handle, + close_mask, + this->timer_id ())); + return 0; +} + +int +Time_Handler::handle_timeout (const ACE_Time_Value &tv, + const void *arg) +{ + long current_count = static_cast<long> (reinterpret_cast<size_t> (arg)); + if (current_count >= 0) + ACE_ASSERT (current_count == the_count); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("[%x] Timer id %d with count #%d|%d timed out at %d!\n"), + this, + this->timer_id (), + the_count, + current_count, + tv.sec ())); + + if (current_count == long (ACE_MAX_TIMERS - 1)) + done = 1; + else if (the_count == ACE_MAX_TIMERS - 1) + { + done = 1; + return -1; + } + else if (current_count == -1) + { + int result = ACE_Reactor::instance ()->reset_timer_interval (this->timer_id (), + ACE_Time_Value (the_count + 1)); + if (result == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error resetting timer interval\n"))); + } + the_count += (1 + odd); + return 0; +} + +long +Time_Handler::timer_id (void) const +{ + return this->timer_id_; +} + +void +Time_Handler::timer_id (long t) +{ + this->timer_id_ = t; +} + +static void +test_registering_all_handlers (void) +{ + ACE_Trace t (ACE_TEXT ("test_registering_all_handler"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt[ACE_MAX_TIMERS]; + long t_id[ACE_MAX_TIMERS]; + + for (size_t i = 0; i < ACE_MAX_TIMERS; i++) + { + t_id[i] = + ACE_Reactor::instance ()->schedule_timer (&rt[i], + (const void *) i, + ACE_Time_Value (2 * i + 1)); + ACE_ASSERT (t_id[i] != -1); + rt[i].timer_id (t_id[i]); + } + + while (!done) + ACE_Reactor::instance ()->handle_events (); +} + +static void +test_registering_one_handler (void) +{ + ACE_Trace t (ACE_TEXT ("test_registering_one_handler"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt[ACE_MAX_TIMERS]; + long t_id[ACE_MAX_TIMERS]; + + done = 0; + the_count = 0; + + for (size_t i = 0; i < ACE_MAX_TIMERS; i++) + { + t_id[i] = + ACE_Reactor::instance ()->schedule_timer (&rt[0], + (const void *) i, + ACE_Time_Value (2 * i + 1)); + ACE_ASSERT (t_id[i] != -1); + } + + while (!done) + ACE_Reactor::instance ()->handle_events (); +} + +static void +test_canceling_odd_timers (void) +{ + ACE_Trace t (ACE_TEXT ("test_canceling_odd_timers"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt[ACE_MAX_TIMERS]; + long t_id[ACE_MAX_TIMERS]; + + done = 0; + the_count = 1; + odd = 1; + + for (size_t i = 0; i < ACE_MAX_TIMERS; i++) + { + t_id[i] = ACE_Reactor::instance ()->schedule_timer (&rt[i], + (const void *) i, + ACE_Time_Value (2 * i + 1)); + ACE_ASSERT (t_id[i] != -1); + rt[i].timer_id (t_id[i]); + } + + for (size_t j = 0; (u_long) j < ACE_MAX_TIMERS; j++) + // Cancel handlers with odd numbered timer ids. + if (ACE_ODD (rt[j].timer_id ())) + { + int result = + ACE_Reactor::instance ()->cancel_timer (rt[j].timer_id ()); + if (result == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error cancelling timer\n"))); + } + + while (!done) + ACE_Reactor::instance ()->handle_events (); +} + +static void +test_resetting_timer_intervals (void) +{ + ACE_Trace t (ACE_TEXT ("test_resetting_timer_intervals"), + __LINE__, + ACE_TEXT_CHAR_TO_TCHAR (__FILE__)); + Time_Handler rt; + long t_id; + + done = 0; + the_count = 0; + odd = 0; + + t_id = + ACE_Reactor::instance ()->schedule_timer + (&rt, + (const void *) -1, + ACE_Time_Value (1), + // Start off by making this an interval timer. + ACE_Time_Value (1)); + + ACE_ASSERT (t_id != -1); + rt.timer_id (t_id); + + while (!done) + ACE_Reactor::instance ()->handle_events (); +} + +// If any command line arg is given, run the test with high res timer +// queue. Else run it normally. +int +run_main (int argc, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Timer_Test")); + + if (argc > 1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Running with high-res timer queue\n"))); + ACE_Reactor *r = ACE_Reactor::instance (); + (void) ACE_High_Res_Timer::global_scale_factor (); + r->timer_queue ()->gettimeofday (&ACE_High_Res_Timer::gettimeofday_hr); + } + + // Register all different handlers, i.e., one per timer. + test_registering_all_handlers (); + + // Now try multiple timers for ONE event handler (should produce the + // same result). + test_registering_one_handler (); + + // Try canceling handlers with odd numbered timer ids. + test_canceling_odd_timers (); + + // Make sure <reset_timer_inverval> works. + test_resetting_timer_intervals (); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Reactors_Test.cpp b/ACE/tests/Reactors_Test.cpp new file mode 100644 index 00000000000..0354483105d --- /dev/null +++ b/ACE/tests/Reactors_Test.cpp @@ -0,0 +1,263 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reactors_Test.cpp +// +// = DESCRIPTION +// This is a test that performs a torture test of multiple +// <ACE_Reactors> and <ACE_Tasks> in the same process. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu>, +// Detlef Becker <Detlef.Becker@med.siemens.de>, and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Task.h" +#include "ace/Reactor.h" +#include "ace/Atomic_Op.h" +#include "ace/Recursive_Thread_Mutex.h" + +ACE_RCSID(tests, Reactors_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +ACE_Thread_Manager *thr_mgr; + +static const int MAX_TASKS = 20; + +class Test_Task : public ACE_Task<ACE_MT_SYNCH> + // = TITLE + // Exercise the tasks. +{ +public: + // = Initialization and termination methods. + Test_Task (void); + ~Test_Task (void); + + //FUZZ: disable check_for_lack_ACE_OS + // = Task hooks. + virtual int open (void *args = 0); + virtual int close (u_long flags = 0); + virtual int svc (void); + //FUZZ: enable check_for_lack_ACE_OS + + // = Event Handler hooks. + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE fd, + ACE_Reactor_Mask close_mask); +private: + size_t handled_; + // Number of iterations handled. + + static int task_count_; + // Number of tasks running. +}; + +// Static data member initialization. +int Test_Task::task_count_ = 0; + +static ACE_Atomic_Op<ACE_Thread_Mutex, int> done_count = MAX_TASKS * 2; + + + +static ACE_Recursive_Thread_Mutex recursive_lock; + +Test_Task::Test_Task (void) + : handled_ (0) +{ + ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, recursive_lock); + + Test_Task::task_count_++; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) TT+ Test_Task::task_count_ = %d\n"), + Test_Task::task_count_)); +} + +Test_Task::~Test_Task (void) +{ + ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, recursive_lock); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) TT- Test_Task::task_count_ = %d\n"), + Test_Task::task_count_)); + + ACE_ASSERT (Test_Task::task_count_ == 0); +} + +int +Test_Task::open (void *args) +{ + this->reactor (reinterpret_cast<ACE_Reactor *> (args)); + return this->activate (THR_NEW_LWP); +} + +int +Test_Task::close (u_long) +{ + ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, recursive_lock, -1); + + Test_Task::task_count_--; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) close Test_Task::task_count_ = %d\n"), + Test_Task::task_count_)); + + ACE_ASSERT (Test_Task::task_count_ >= 0); + + return 0; +} + +int +Test_Task::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) svc\n"))); + + for (size_t i = 0; i < ACE_MAX_ITERATIONS; i++) + { + ACE_OS::thr_yield (); + + // Only wait up to 10 milliseconds to notify the Reactor. + ACE_Time_Value timeout (0, 10 * 1000); + + if (this->reactor ()->notify (this, + ACE_Event_Handler::READ_MASK, + &timeout) == -1) + { + if (errno == ETIME) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("notify() timed out"))); + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("notify")), + -1); + } + } + + return 0; +} + +int +Test_Task::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + return 0; +} + +int +Test_Task::handle_input (ACE_HANDLE) +{ + this->handled_++; + + if (this->handled_ == ACE_MAX_ITERATIONS) + { + done_count--; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) handle_input, handled_ = %d, done_count = %d\n"), + this->handled_, + done_count.value ())); + } + + ACE_OS::thr_yield (); + return -1; +} + +static void * +worker (void *args) +{ + ACE_Reactor *reactor = reinterpret_cast<ACE_Reactor *> (args); + + // Make this thread the owner of the Reactor's event loop. + reactor->owner (ACE_Thread::self ()); + + // Use a timeout to inform the Reactor when to shutdown. + ACE_Time_Value timeout (4); + + for (;;) + switch (reactor->handle_events (timeout)) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("reactor")), + 0); + /* NOTREACHED */ + case 0: + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Reactor shutdown\n"))); + return 0; + } + + ACE_NOTREACHED (return 0); +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactors_Test")); + +#if defined (ACE_HAS_THREADS) + ACE_ASSERT (ACE_LOG_MSG->op_status () != -1); + + thr_mgr = ACE_Thread_Manager::instance (); + + ACE_Reactor reactor; + ACE_ASSERT (ACE_LOG_MSG->op_status () != -1); + + Test_Task tt1[MAX_TASKS]; + Test_Task tt2[MAX_TASKS]; + + // Activate all of the Tasks. + + for (int i = 0; i < MAX_TASKS; i++) + { + tt1[i].open (ACE_Reactor::instance ()); + tt2[i].open (&reactor); + } + + // Spawn two threads each running a different reactor. + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (worker), + (void *) ACE_Reactor::instance (), + THR_BOUND | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn")), + -1); + + else if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (worker), (void *) &reactor, + THR_BOUND | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn")), + -1); + + if (ACE_Thread_Manager::instance ()->wait () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("wait")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) all threads are finished \n"))); + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Reader_Writer_Test.cpp b/ACE/tests/Reader_Writer_Test.cpp new file mode 100644 index 00000000000..d268bed118f --- /dev/null +++ b/ACE/tests/Reader_Writer_Test.cpp @@ -0,0 +1,293 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reader_Writer_Test.cpp +// +// = DESCRIPTION +// This test program verifies the functionality of the ACE_OS +// implementation of readers/writer locks on Win32 and Posix +// pthreads. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Doug C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Get_Opt.h" +#include "ace/Atomic_Op.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Guard_T.h" +#include "ace/RW_Thread_Mutex.h" +#include "ace/Time_Value.h" + +ACE_RCSID(tests, Reader_Writer_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +// Default number of iterations. +#if defined (VXWORKS) + // So the test doesn't run for too long . . . + static size_t n_iterations = 25; +#else + static size_t n_iterations = 50; +#endif /* VXWORKS */ + +// Default number of loops. +#if defined (VXWORKS) + // thr_yield () and/or thr_equal () are expensive on VxWorks, apparently. + static size_t n_loops = 10; +#else + static size_t n_loops = 100; +#endif /* VXWORKS */ + +// Default number of readers. +static size_t n_readers = 6; + +// Default number of writers. +static size_t n_writers = 4; + +// Thread id of last writer. +static ACE_thread_t shared_data; + +// Lock for shared_data. +static ACE_RW_Thread_Mutex rw_mutex; + +// Count of the number of readers and writers. +static ACE_Atomic_Op<ACE_Thread_Mutex, long> current_readers; +static ACE_Atomic_Op<ACE_Thread_Mutex, long> current_writers; + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-r n_readers] [-w n_writers] [-n iteration_count]\n"))); + ACE_OS::exit (1); +} + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("r:w:n:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'r': + n_readers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'w': + n_writers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + n_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + print_usage_and_die (); + break; + } +} + +// Iterate <n_iterations> each time checking that nobody modifies the data +// while we have a read lock. + +static void * +reader (void *) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) reader starting\n"))); + + // We use a random pause, around 2msec with 1msec jittering. + int usecs = 1000 + ACE_OS::rand() % 2000; + ACE_Time_Value pause(0, usecs); + + for (size_t iterations = 1; iterations <= n_iterations; iterations++) + { + ACE_OS::sleep (pause); + ACE_Read_Guard<ACE_RW_Thread_Mutex> g (rw_mutex); + // int n = ++current_readers; + // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) I'm reader number %d\n"), n)); + + if (current_writers > 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) writers found!!!\n"))); + + ACE_thread_t data = shared_data; + + for (size_t loop = 1; loop <= n_loops; loop++) + { + ACE_Thread::yield (); + + if (!ACE_OS::thr_equal (shared_data, data)) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) somebody changed %d to %d\n"), + data, shared_data)); + } + + int result = rw_mutex.tryacquire_write_upgrade (); + + if (result == 0) + { + --current_readers; + ++current_writers; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) upgraded to write lock!\n"))); + + ACE_thread_t self = ACE_Thread::self (); + + shared_data = self; + data = self; + + for (size_t loop = 1; loop <= n_loops; loop++) + { + if (ACE_OS::thr_equal (shared_data, data) == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) upgraded writer error: somebody changed %d to %d\n"), + data, + shared_data)); + } + + --current_writers; + // we were a writer + } + else if (result == -1 && errno == EBUSY) + { + --current_readers; + // we were still a reader + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) could not upgrade to write lock!\n"))); + + } + else // result == -1 + { + // These #ifs should generally match the logic in OS_NS_Thread.inl. + +# if defined (ACE_HAS_PTHREADS_UNIX98_EXT) || !defined (ACE_LACKS_RWLOCK_T) + // In this case we have native RW locks support, but native RW + // locks may not support upgrading! + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("can't upgrade write lock"), + 1)); +# else + // In this case we do not have native RW locks, but therefore the + // emulation, which supports upgradable write locks. + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("failure in upgrading to write lock"), + 1)); +# endif /* ACE_HAS_PTHREADS_UNIX98_EXT || !ACE_LACKS_RWLOCK_T */ + } + //ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) done with reading guarded data\n"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) reader finished %d iterations at %T\n"), + iterations)); + } + return 0; +} + +// Iterate <n_iterations> each time modifying the global data +// and checking that nobody steps on it while we can write it. + +static void * +writer (void *) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) writer starting\n"))); + + // We use a random pause, around 2msec with 1msec jittering. + int usecs = 1000 + ACE_OS::rand() % 2000; + ACE_Time_Value pause(0, usecs); + + for (size_t iterations = 1; iterations <= n_iterations; iterations++) + { + ACE_OS::sleep (pause); + + ACE_Write_Guard<ACE_RW_Thread_Mutex> g (rw_mutex); + + ++current_writers; + + if (current_writers > 1) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) other writers found!!!\n"))); + + if (current_readers > 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) readers found!!!\n"))); + + ACE_thread_t self = ACE_Thread::self (); + + shared_data = self; + + for (size_t loop = 1; loop <= n_loops; loop++) + { + ACE_Thread::yield (); + + if (!ACE_OS::thr_equal (shared_data, self)) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) somebody wrote on my data %d\n"), + shared_data)); + } + + --current_writers; + + ACE_DEBUG((LM_DEBUG, ACE_TEXT (" (%t) write %d done at %T\n"), iterations)); + } + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Spawn off threads. + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Reader_Writer_Test")); + +#if defined (ACE_HAS_THREADS) + parse_args (argc, argv); + + current_readers = 0; // Possibly already done + current_writers = 0; // Possibly already done + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) main thread starting\n"))); + + if (ACE_Thread_Manager::instance ()->spawn_n (n_readers, + ACE_THR_FUNC (reader), + 0, + THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), 1); + else if (ACE_Thread_Manager::instance ()->spawn_n (n_writers, + ACE_THR_FUNC (writer), + 0, + THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), 1); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) exiting main thread\n"))); +#else + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Recursive_Condition_Bug_Test.cpp b/ACE/tests/Recursive_Condition_Bug_Test.cpp new file mode 100644 index 00000000000..26478456bd4 --- /dev/null +++ b/ACE/tests/Recursive_Condition_Bug_Test.cpp @@ -0,0 +1,193 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Recursive_Condition_Bug_Test.cpp +// +// = DESCRIPTION +// This test program validates the functionality of the +// ACE_Condition<ACE_Recursive_Thread_Mutex> template +// specialization when combined with the +// ACE_Thread_Timer_Queue_Adapter on Win32 and Posix pthreads. +// It was added to test for bugs with the ACE_OS recursive +// condition implementation. +// +// = AUTHOR +// Leonid Kvetnyi <leonidk@nice.com> and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Task_T.h" +#include "ace/Activation_Queue.h" +#include "ace/Timer_Heap.h" +#include "ace/Timer_Queue_Adapters.h" +#include "ace/Condition_Recursive_Thread_Mutex.h" + +ACE_RCSID (tests, + Recursive_Condition_Bug_Test, + "$Id$") + +#if defined (ACE_HAS_THREADS) + +// Number of iterations for the performance tests. +static int max_iterations = 30; + +typedef ACE_Thread_Timer_Queue_Adapter<ACE_Timer_Heap> Thread_Timer_Queue; + +class Test_Handler; + +class Test_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + virtual int svc (void) + { + while (--max_iterations > 0) + { + // dequeue the next object + ACE_Message_Block * mb = 0; + + if (this->getq (mb) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("getq failed")), + -1); + + Test_Handler *handler = reinterpret_cast<Test_Handler *> (mb->base ()); + mb->release (); + + ACE_Time_Value timeout = ACE_OS::gettimeofday () + ACE_Time_Value (1, 0); + + if (timer_queue_.schedule (reinterpret_cast<ACE_Event_Handler *> (handler), + this, + timeout) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("schedule failed")), + -1); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) %d iteration(s) remain\n"), + max_iterations)); + } + + timer_queue_.deactivate (); + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%t) leaving the Test_task\n"))); + return 0; + } + + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0) + { + //FUZZ: enable check_for_lack_ACE_OS + if (ACE_Task<ACE_MT_SYNCH>::activate (THR_NEW_LWP, 1) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Test_Task::activate")), + -1); + if (0 != timer_queue_.activate ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Test_Task::queue activate")), + -1); + return 0; + } + +private: + Thread_Timer_Queue timer_queue_; +}; + +class Test_Handler : public ACE_Event_Handler +{ +public: + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Test_Handler::handle_timeout\n"))); + + void *nc_arg = const_cast<void *> (arg); + Test_Task *test_task = + reinterpret_cast<Test_Task *> (nc_arg); + ACE_Message_Block *mb = 0; + ACE_NEW_MALLOC_RETURN (mb, + static_cast<ACE_Message_Block *> (ACE_Allocator::instance()->malloc (sizeof (ACE_Message_Block))), + ACE_Message_Block (sizeof (*this), // size + ACE_Message_Block::MB_DATA, // type + 0, // cont + (char *) this, // data + 0, + 0, + ACE_DEFAULT_MESSAGE_BLOCK_PRIORITY, + ACE_Time_Value::zero, + ACE_Time_Value::max_time, + 0, + ACE_Allocator::instance()), // data + -1); + + test_task->putq (mb); + return 0; + } +}; + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Recursive_Condition_Bug_Test")); + +#if defined (ACE_HAS_THREADS) + + // Timer queue usage. + + Test_Handler handler; + Test_Task task; + + if (0 != task.open ()) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("open")), + -1); + + ACE_Message_Block *mb = 0; + ACE_NEW_MALLOC_RETURN (mb, + static_cast<ACE_Message_Block *> (ACE_Allocator::instance()->malloc (sizeof (ACE_Message_Block))), + ACE_Message_Block (sizeof (handler), // size + ACE_Message_Block::MB_DATA, // type + 0, // cont + (char *) &handler, + 0, + 0, + ACE_DEFAULT_MESSAGE_BLOCK_PRIORITY, + ACE_Time_Value::zero, + ACE_Time_Value::max_time, + 0, + ACE_Allocator::instance()), // data + -1); + + if (-1 == task.putq (mb)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("putq")), + -1); + + if (ACE_Thread_Manager::instance ()->wait () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "wait on Thread_Manager failed"), + -1); +#else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE doesn't support recursive condition variables on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Recursive_Condition_Test.cpp b/ACE/tests/Recursive_Condition_Test.cpp new file mode 100644 index 00000000000..5576b2cf674 --- /dev/null +++ b/ACE/tests/Recursive_Condition_Test.cpp @@ -0,0 +1,308 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Recursive_Condition_Test.cpp +// +// = DESCRIPTION +// This test program validates the functionality of the +// ACE_Condition<ACE_Recursive_Thread_Mutex> template +// specialization when combined with the +// ACE_Thread_Timer_Queue_Adapter on Win32 and Posix pthreads. +// +// = AUTHOR +// Stephen Howard <stephen.e.howard@lmco.com> and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Event_Handler.h" +#include "ace/Log_Msg.h" +#include "ace/Thread_Manager.h" +#include "ace/Timer_Heap.h" +#include "ace/Timer_Queue_Adapters.h" + +ACE_RCSID(tests, Recursive_Condition_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +typedef ACE_Thread_Timer_Queue_Adapter<ACE_Timer_Heap> Thread_Timer_Queue; + +class Test_Handler : public ACE_Event_Handler +{ +public: + Test_Handler (void) : nr_expirations_ (0) {} + int nr_expirations (void) { return this->nr_expirations_; } + + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Test_Handler::handle_timeout\n"))); + ++this->nr_expirations_; + + void *nc_arg = const_cast<void *> (arg); + Thread_Timer_Queue *timer_queue = + reinterpret_cast<Thread_Timer_Queue *> (nc_arg); + + ACE_Time_Value timeout = ACE_OS::gettimeofday () + ACE_Time_Value (1, 0); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) scheduling new timer 1 sec from now\n"))); + if (timer_queue->schedule (this, timer_queue, timeout) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("schedule failed"))); + + return 0; + } + +private: + int nr_expirations_; +}; + +// These are for the basic functionality tests. +ACE_SYNCH_RECURSIVE_MUTEX mutex_; +ACE_SYNCH_RECURSIVE_CONDITION condition_(mutex_); +// Test driver sets this to non-zero before spawning and to zero for waiter. +int protected_int = 0; + +static ACE_THR_FUNC_RETURN +waiter (void *) +{ + if (mutex_.acquire () != 0) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("acquire")), 0); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) waiting for cv signal...\n"))); + if (condition_.wait () == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) woken up!!!\n"))); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), ACE_TEXT ("wait"))); + + int copy_int = protected_int; // Copy it in case it's erroneously changing + if (copy_int != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) waiter found protected_int %d\n"), + copy_int)); + + if (mutex_.release () != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), ACE_TEXT ("release"))); + + return 0; +} + +static int +test_1 (void) +{ + protected_int = 1; + if (ACE_Thread_Manager::instance()->spawn (waiter) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 1 spawn")), + 1); + + ACE_OS::sleep (2); + if (mutex_.acquire () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 1 mutex acquire")), + 1); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) signaling condition...\n"))); + protected_int = 0; + if (condition_.signal () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 1 signal"))); + + if (mutex_.release () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 1 release"))); + + ACE_Thread_Manager::instance ()->wait (); + return 0; +} + +static int +test_2 (void) +{ + protected_int = 1; + if (ACE_Thread_Manager::instance()->spawn (waiter) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 2 spawn")), + 1); + + ACE_OS::sleep (2); + if (mutex_.acquire () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 2 mutex acquire")), + 1); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) signaling condition...\n"))); + if (condition_.signal () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("test 2 signal"))); + + // Wait to clear protected_int to be sure cv properly reacquires the + // mutex before returning control to caller. + ACE_OS::sleep(2); + protected_int = 0; + if (mutex_.release () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 2 release"))); + + ACE_Thread_Manager::instance ()->wait (); + return 0; +} + +static int +test_3 (void) +{ + protected_int = 1; + if (ACE_Thread_Manager::instance()->spawn_n (4, waiter) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("test 3 spawn")), 1); + + ACE_OS::sleep (2); + if (mutex_.acquire () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 3 mutex acquire")), + 1); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) broadcasting condition...\n"))); + if (condition_.broadcast () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 3 broadcast"))); + protected_int = 0; + if (mutex_.release () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 3 release"))); + + ACE_Thread_Manager::instance ()->wait (); + + return 0; +} + +static int +test_4 (void) +{ + const int recurse_count = 3; + + protected_int = recurse_count; + if (ACE_Thread_Manager::instance()->spawn (waiter) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("spawn")), 1); + + ACE_OS::sleep (2); + int i; + for (i = 0; i < recurse_count; ++i) + if (mutex_.acquire () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("pass %d, %p\n"), + i + 1, + ACE_TEXT ("recursive acquire")), + 1); + + // Don't report a failure if the mutex doesn't offer a view of the + // recursion count. + int nesting_level = mutex_.get_nesting_level (); + if (!(nesting_level == -1 && errno == ENOTSUP) && nesting_level != i) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT("test 4 nesting level %d;") + ACE_TEXT (" should be %d\n"), + nesting_level, i), + 1); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) signaling condition...\n"))); + if (condition_.signal () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("test 4 signal")), + 1); + + for (i = 0; i < recurse_count; ++i) + { + // Only decrement - be sure all the waiting threads are not released + // before we release the mutex the correct number of times. + --protected_int; + mutex_.release (); + } + + // The waiter thread will acquire the mutex as a result of the releases + // above... don't check the nesting level until waiter() has had a chance + // to wake up, acquire, and release the mutex. + ACE_Thread_Manager::instance ()->wait (); + + nesting_level = mutex_.get_nesting_level (); + if (!(nesting_level == -1 && errno == ENOTSUP) && nesting_level != 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%t) nesting level %d; should be 0\n"), + nesting_level), + 1); + + return 0; +} +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Recursive_Condition_Test")); + +#if defined (ACE_HAS_THREADS) + + int status = 0; + + /* Test 1 - Simple test */ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test 1...\n"))); + if (test_1 () != 0) + ++status; + + /* Test #2 - Sleep 2 seconds before releasing mutex */ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test 2...\n"))); + if (test_2 () != 0) + ++status; + + /* Test #3 - One main thread - 4 subthreads */ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test 3...\n"))); + if (test_3 () != 0) + ++status; + + /* Test #4 - Multiple calls to mutex_.acquire and mutex_.release */ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test 4...\n"))); + if (test_4 () != 0) + ++status; + + // Timer queue usage. + Thread_Timer_Queue timer_queue; + Test_Handler handler; + if (0 != timer_queue.activate ()) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), ACE_TEXT ("activate"))); + ++status; + } + + ACE_Time_Value timeout = + ACE_OS::gettimeofday() + ACE_Time_Value (1, 0); + + if (-1 == timer_queue.schedule (&handler, &timer_queue, timeout)) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), ACE_TEXT ("schedule"))); + ++status; + } + + ACE_OS::sleep (10); + timer_queue.deactivate (); + timer_queue.wait (); + // Scheduling every second, waiting 10 seconds, should get at least 9 + int expirations = handler.nr_expirations (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Caught %d timer expirations\n"), + expirations)); + if (expirations < 9) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Should have caught at least 9\n"))); + +#else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE doesn't support recursive condition variables on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Recursive_Mutex_Test.cpp b/ACE/tests/Recursive_Mutex_Test.cpp new file mode 100644 index 00000000000..fafe771551a --- /dev/null +++ b/ACE/tests/Recursive_Mutex_Test.cpp @@ -0,0 +1,389 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Recursive_Mutex_Test.cpp +// +// = DESCRIPTION +// This test program verifies the functionality of the ACE_OS +// implementation of recursive mutexes on Win32 and Posix +// pthreads. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Get_Opt.h" +#include "ace/Thread_Manager.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Recursive_Thread_Mutex.h" + +ACE_RCSID(tests, Recursive_Mutex_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#include "ace/Guard_T.h" + +// For all platforms except for Windows use the +// ACE_Recursive_Thread_Mutex. Since Windows only supports timed +// recursive process mutexes and not timed recursive thread mutexes, +// use ACE_Process_Mutex. +#if defined (ACE_HAS_WTHREADS) +# include "ace/Process_Mutex.h" + typedef ACE_Process_Mutex ACE_TEST_MUTEX; +#else +# include "ace/Thread_Mutex.h" + typedef ACE_Recursive_Thread_Mutex ACE_TEST_MUTEX; +#endif + +#if !defined (ACE_HAS_MUTEX_TIMEOUTS) +static int reported_notsup = 0; +#endif /* ACE_HAS_MUTEX_TIMEOUTS */ + +// Total number of iterations. +static int const n_iterations = 100; +static size_t n_threads = ACE_MAX_THREADS; + +// ACE_Recursive_Thread_Mutex::get_nesting_level() will return a +// meaningful value. +static bool nesting_level_supported = false; + +static void +test_recursion_depth (int nesting_level, + ACE_TEST_MUTEX *rm) +{ + if (nesting_level < n_iterations) + { +#if !defined (ACE_HAS_WTHREADS) + // This will work for Windows, too, if ACE_TEST_MUTEX is + // ACE_Recursive_Thread_Mutex instead of ACE_Process_Mutex. + if (nesting_level_supported + && nesting_level != 0 + && nesting_level != rm->get_nesting_level ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Pre-mutex acquire nesting ") + ACE_TEXT ("levels do not match.\n"))); + } +#endif /* !ACE_HAS_WTHREADS */ + int result = rm->acquire (); + ACE_ASSERT (result == 0); +#if !defined (ACE_HAS_WTHREADS) + if (nesting_level_supported + && (nesting_level + 1) != rm->get_nesting_level ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Post-mutex acquire nesting ") + ACE_TEXT ("levels do not match.\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = acquired, nesting = %d, thread id = %u\n"), + rm->get_nesting_level (), + rm->get_thread_id ())); +#endif /* !ACE_HAS_WTHREADS */ + + test_recursion_depth (nesting_level + 1, + rm); + +#if !defined (ACE_HAS_WTHREADS) + if (nesting_level_supported + && (nesting_level + 1) != rm->get_nesting_level ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Post recursion nesting ") + ACE_TEXT ("levels do not match.\n"))); + } +#endif /* !ACE_HAS_WTHREADS */ + result = rm->release (); + ACE_ASSERT (result == 0); +#if !defined (ACE_HAS_WTHREADS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = released, nesting = %d, thread id = %u\n"), + rm->get_nesting_level (), + rm->get_thread_id ())); + + if (nesting_level_supported + && nesting_level != 0 + && nesting_level != rm->get_nesting_level ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Post-mutex release nesting ") + ACE_TEXT ("levels do not match.\n"))); + } +#endif /* !ACE_HAS_WTHREADS */ + } +} + +static void +test_timed_wait (int nesting_level, + ACE_TEST_MUTEX *rm) +{ + // Make sure that we're inside of a recursive level. + if (nesting_level == 0) + test_timed_wait (nesting_level + 1, + rm); + else + { + ACE_OS::srand ((u_int) ACE_OS::time (0)); + + for (size_t i = 0; i < ACE_MAX_ITERATIONS / 2; i++) + { + int result = 0; + + // First attempt to acquire the mutex with a timeout to verify + // that mutex timeouts are working. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = trying timed acquire on ") + ACE_TEXT ("iteration %d\n"), + i)); + + ACE_Time_Value delta (1, 0); // One second timeout + ACE_Time_Value timeout = ACE_OS::gettimeofday (); + timeout += delta; // Must pass absolute time to acquire(). + + if (rm->acquire (timeout) != 0) + { + if (errno == ETIME) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = mutex acquisition ") + ACE_TEXT ("timed out\n"))); + else if (errno == ENOTSUP) + { +#if !defined (ACE_HAS_MUTEX_TIMEOUTS) + if (!reported_notsup) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) %p, but ACE_HAS_MUTEX_TIMEOUTS is not defined - Ok\n"), + ACE_TEXT ("mutex timed acquire"))); + reported_notsup = 1; + } +#else + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p - maybe ACE_HAS_MUTEX_TIMEOUTS should not be defined?\n"), + ACE_TEXT ("mutex timed acquire"))); +#endif /* ACE_HAS_MUTEX_TIMEOUTS */ + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("mutex timeout failed\n"))); + return; + } + } + else + { + result = rm->release (); + ACE_ASSERT (result == 0); + } + + // Now try the standard mutex. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = trying to acquire on iteration %d\n"), + i)); + result = rm->acquire (); + ACE_ASSERT (result == 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = acquired on iteration %d\n"), + i)); + + // Sleep for a random amount of time between 0 and 2 seconds. + // Note that it's ok to use rand() here because we are running + // within the critical section defined by the Thread_Mutex. + ACE_OS::sleep (ACE_OS::rand () % 2); + + result = rm->release (); + ACE_ASSERT (result == 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = released on iteration %d\n"), + i)); + + // Basic ACE_Guard usage - automatically acquire the mutex on + // guard construction and automatically release it on + // destruction. + { + // Construct an ACE_Guard to implicitly acquire the mutex. + ACE_Guard<ACE_TEST_MUTEX> guard (*rm); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // ACE_Guard object is destroyed when exiting scope and guard + // destructor automatically releases mutex. + } + + // Use an ACE_Guard to automatically acquire a mutex, but release + // the mutex early. + { + // Construct an ACE_Guard to implicitly acquire the mutex. + ACE_Guard<ACE_TEST_MUTEX> guard (*rm); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // Release the mutex since we no longer need it. + guard.release (); + ACE_ASSERT (guard.locked () == 0); + + // Do something else which does not require the mutex to be locked. + // ... + + // ACE_Guard object's destructor will not release the mutex. + } + + // Use an ACE_Guard to automatically acquire a mutex, but + // relinquish ownership of the lock so that the mutex is not + // automatically released on guard destruction. This is useful + // when an operation might not release the mutex in some + // conditions, in which case responsibility for releasing it is + // passed to someone else. + { + // Construct an ACE_Guard to implicitly acquire the mutex. + ACE_Guard<ACE_TEST_MUTEX> guard (*rm); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // Relinquish ownership of the mutex lock. Someone else must + // now release it. + guard.disown (); + ACE_ASSERT (guard.locked () == 0); + + // ACE_Guard object's destructor will not release the mutex. + } + // We are now responsible for releasing the mutex. + result = rm->release (); + ACE_ASSERT (result == 0); + + // Construct an ACE_Guard without automatically acquiring the lock. + { + // Construct an ACE_Guard object without automatically + // acquiring the mutex or taking ownership of an existing + // lock. The third parameter tells the guard that the mutex + // has not been locked. + ACE_Guard<ACE_TEST_MUTEX> guard (*rm, 0, 0); + ACE_ASSERT (guard.locked () == 0); + + // Conditionally acquire the mutex. + if (i % 2 == 0) + { + guard.acquire (); + ACE_ASSERT (guard.locked () != 0); + } + + // Perform some operation that might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // ACE_Guard object is destroyed when exiting scope and guard + // destructor automatically releases if it was acquired above. + } + + // Use an ACE_Guard to take ownership of a previously acquired + // mutex. + timeout = ACE_OS::gettimeofday (); + timeout += delta; // Must pass absolute time to acquire(). + if (rm->acquire (timeout) == 0) + { + // Construct an ACE_Guard object without automatically + // acquiring the mutex, but instead take ownership of the + // existing lock. The third parameter tells the guard that + // the mutex has already been locked. + ACE_Guard<ACE_TEST_MUTEX> guard (*rm, 0, 1); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // ACE_Guard object is destroyed when exiting scope and guard + // destructor automatically releases mutex. + } + } + + return; + } +} + +static void * +recursion_worker (void *arg) +{ + ACE_TEST_MUTEX *rm = + reinterpret_cast<ACE_TEST_MUTEX *> (arg); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%P|%t) Starting test of recursion depth\n"))); + test_recursion_depth (0, rm); + + return 0; +} + +static void * +timed_worker (void *arg) +{ + ACE_TEST_MUTEX *rm = + reinterpret_cast<ACE_TEST_MUTEX *> (arg); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%P|%t) Starting test of timed wait\n"))); + test_timed_wait (0, rm); + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Recursive_Mutex_Test")); + +#if defined (ACE_HAS_THREADS) + if (argc > 1) + { + n_threads = ACE_OS::atoi (argv[1]); + } + + ACE_TEST_MUTEX rm; + +#if !defined (ACE_HAS_WTHREADS) + // This will work for Windows, too, if ACE_TEST_MUTEX is + // ACE_Recursive_Thread_Mutex instead of ACE_Process_Mutex. + nesting_level_supported = + (rm.get_nesting_level () != -1 || errno != ENOTSUP); +#endif /* !ACE_HAS_WTHREADS */ + + ACE_Thread_Manager::instance ()->spawn_n (n_threads, + ACE_THR_FUNC (recursion_worker), + (void *) &rm); + ACE_Thread_Manager::instance ()->wait (); + + ACE_Thread_Manager::instance ()->spawn_n (n_threads, + ACE_THR_FUNC (timed_worker), + (void *) &rm); + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE doesn't support recursive process ") + ACE_TEXT ("mutexes on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Refcounted_Auto_Ptr_Test.cpp b/ACE/tests/Refcounted_Auto_Ptr_Test.cpp new file mode 100644 index 00000000000..f62a3b1cb24 --- /dev/null +++ b/ACE/tests/Refcounted_Auto_Ptr_Test.cpp @@ -0,0 +1,551 @@ +// $Id$ + +//============================================================================= +/** + * @file Refcounted_Auto_Ptr_Test.cpp + * + * $Id$ + * + * This example tests the <ACE_Refcounted_Auto_Ptr> and illustrates + * how they may be dispersed between multiple threads using an + * implementation of the Active Object pattern, which is available + * at <http://www.cs.wustl.edu/~schmidt/PDF/Act-Obj.pdf>. + * + * + * @author Johnny Tucker <johnny_tucker@yahoo.com> + */ +//============================================================================= + + +#include "test_config.h" +#include "ace/ACE.h" +#include "ace/Task.h" +#include "ace/Message_Queue.h" +#include "ace/Method_Request.h" +#include "ace/Null_Mutex.h" +#include "ace/Activation_Queue.h" +#include "ace/Refcounted_Auto_Ptr.h" +#include "Refcounted_Auto_Ptr_Test.h" + +ACE_RCSID (tests, + Refcounted_Auto_Ptr_Test, + "$Id$") + +ACE_Atomic_Op<ACE_SYNCH_MUTEX, unsigned int> Printer::current_instance_ (0); +ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> Printer::instance_count_ (0); + +Printer::Printer (const char *message) + : message_ (message) +{ + this->which_ = ++Printer::current_instance_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Creating Printer object %d (%C)\n"), + this->which_, + this->message_)); + ++Printer::instance_count_; +} + +Printer::~Printer (void) +{ + --Printer::instance_count_; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Deleting Printer object %d (%C)\n"), + this->which_, + this->message_)); +} + +void +Printer::print (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %C\n"), + this->message_)); +} + +#if defined (ACE_HAS_THREADS) + +typedef ACE_Refcounted_Auto_Ptr<Printer, ACE_Thread_Mutex> Printer_var; + +/** + * @class Scheduler + * + * @brief The scheduler for the Active Object. + * + * This class also plays the role of the Proxy and the Servant + * in the Active Object pattern. Naturally, these roles could + * be split apart from the Scheduler. + */ +class Scheduler : public ACE_Task<ACE_SYNCH> +{ + + friend class Method_Request_print; + friend class Method_Request_end; +public: + // = Initialization and termination methods. + /// Constructor. + Scheduler (Scheduler * = 0); + + //FUZZ: disable check_for_lack_ACE_OS + /// Initializer. + virtual int open (void *args = 0); + + /// Terminator. + virtual int close (u_long flags = 0); + //FUZZ: enable check_for_lack_ACE_OS + + /// Destructor. + virtual ~Scheduler (void); + + // = These methods are part of the Active Object Proxy interface. + void print (Printer_var &printer); + void end (void); + +protected: + /// Runs the Scheduler's event loop, which dequeues <Method_Requests> + /// and dispatches them. + virtual int svc (void); + +private: + // = These are the <Scheduler> implementation details. + ACE_Activation_Queue activation_queue_; + Scheduler *scheduler_; +}; + +/** + * @class Method_Request_print + * + * @brief Reification of the <print> method. + */ +class Method_Request_print : public ACE_Method_Request +{ +public: + Method_Request_print (Scheduler *, + Printer_var &printer); + virtual ~Method_Request_print (void); + + /// This is the entry point into the Active Object method. + virtual int call (void); + +private: + Scheduler *scheduler_; + Printer_var printer_; +}; + +Method_Request_print::Method_Request_print (Scheduler *new_scheduler, + Printer_var &printer) + : scheduler_ (new_scheduler), + printer_ (printer) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_print created\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Printer reference count: %d\n"), + printer_.count ())); +} + +Method_Request_print::~Method_Request_print (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Method_Request_print will be deleted.\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Printer reference count: %d\n"), + printer_.count ())); +} + +int +Method_Request_print::call (void) +{ + // Dispatch the Servant's operation and store the result into the + // Future. + Printer_var temp = printer_; + + temp->print (); + + return 0; +} + +/** + * @class Method_Request_end + * + * @brief Reification of the <end> method. + */ +class Method_Request_end : public ACE_Method_Request +{ +public: + Method_Request_end (Scheduler *new_Prime_Scheduler); + virtual ~Method_Request_end (void); + virtual int call (void); + +private: + Scheduler *scheduler_; +}; + +Method_Request_end::Method_Request_end (Scheduler *scheduler) + : scheduler_ (scheduler) +{ +} + +Method_Request_end::~Method_Request_end (void) +{ +} + +int +Method_Request_end::call (void) +{ + // Shut down the scheduler by deactivating the activation queue's + // underlying message queue - should pop all worker threads off their + // wait and they'll exit. + this->scheduler_->msg_queue ()->deactivate (); + return -1; +} + +// Constructor +// Associates the activation queue with this task's message queue, +// allowing easy access to the message queue for shutting it down +// when it's time to stop this object's service threads. +Scheduler::Scheduler (Scheduler *new_scheduler) + : activation_queue_ (msg_queue ()), scheduler_ (new_scheduler) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Scheduler created\n"))); +} + +// Destructor + +Scheduler::~Scheduler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Scheduler will be destroyed\n"))); +} + +// open + +int +Scheduler::open (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Scheduler open\n"))); + // Become an Active Object. + int num_threads = 3; + return this->activate (THR_BOUND | THR_JOINABLE, num_threads); +} + +// close + +int +Scheduler::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) rundown\n"))); + return 0; +} + +// Service.. + +int +Scheduler::svc (void) +{ + for (;;) + { + // Dequeue the next method request (we use an auto pointer in + // case an exception is thrown in the <call>). + ACE_Method_Request *mo_p = this->activation_queue_.dequeue (); + if (0 == mo_p) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) activation queue shut down\n"))); + break; + } + auto_ptr<ACE_Method_Request> mo (mo_p); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) calling method request\n"))); + // Call it. + if(mo->call () == -1) + break; + + // Destructor automatically deletes it. + } + + return 0; +} + +void +Scheduler::end (void) +{ + this->activation_queue_.enqueue (new Method_Request_end (this)); +} + +// Here's where the work takes place. + +void +Scheduler::print (Printer_var &printer) +{ + this->activation_queue_.enqueue + (new Method_Request_print (this, + printer)); +} + +// Total number of loops. +static int n_loops = 10; + +#endif /* ACE_HAS_THREADS */ + + +// This will be used in a single thread to test the reset and release +// methods. See Bugzilla #1925 for history. + +typedef ACE_Refcounted_Auto_Ptr <Printer, ACE_Null_Mutex> Printer_Ptr; + +static bool expect (const ACE_TCHAR *name, + const Printer_Ptr &ptr, + bool expect_null, + unsigned int expect_which, + int expect_count) +{ + if (ptr.null () != expect_null) + { + if (expect_null) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Expecting: %s null:: ") + ACE_TEXT ("Actual: Printer: %u; Count %d\n"), + name, + ptr->which_, + ptr.count ())); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expecting: %s Printer: %u; Count %d:: ") + ACE_TEXT ("Actual: Null.\n"), + name, + expect_which, + expect_count)); + return false; + } + if (ptr.null ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Expecting: %s null:: Actual: Null.\n"), + name)); + return true; + } + + // Note that count is zero based (0 means one reference, etc.) + bool fail = (expect_which != ptr->which_) || (expect_count != ptr.count ()); + ACE_DEBUG ((fail ? LM_ERROR : LM_DEBUG, + ACE_TEXT ("Expecting: %s Printer: %u; Count %d:: ") + ACE_TEXT ("Actual: Printer: %u; Count %d\n"), + name, + expect_which, + expect_count, + ptr->which_, + ptr.count ())); + return !fail; +} + +static int test_reset_release (void) +{ + int errors = 0; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test copy constructor\n"))); + Printer_Ptr bar(new Printer ("1")); + Printer_Ptr fum = bar; + if (!expect (ACE_TEXT ("bar"), bar, false, 1, 1)) + ++errors; + if (!expect (ACE_TEXT ("fum"), fum, false, 1, 1)) + ++errors; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test reset to a new value\n"))); + bar.reset (new Printer ("2")); + if (!expect (ACE_TEXT ("bar"), bar, false, 2, 0)) + ++errors; + if (!expect (ACE_TEXT ("fum"), fum, false, 1, 0)) + ++errors; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test release\n"))); + Printer_Ptr fie(new Printer ("3")); + Printer_Ptr foe = fie; + foe.release(); + if (!expect (ACE_TEXT ("fie"), fie, false, 3, 0)) + ++errors; + if (!expect (ACE_TEXT ("foe"), foe, true, 0, 0)) + ++errors; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test assignment to null\n"))); + Printer_Ptr fee(new Printer ("4")); + Printer_Ptr eraser; + fee = eraser; + if (!expect (ACE_TEXT ("fee"), fee, true, 0, 0)) + ++errors; + if (!expect (ACE_TEXT ("eraser"), eraser, true, 0, 0)) + ++errors; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test assignment to value\n"))); + Printer_Ptr fix(new Printer ("5")); + Printer_Ptr fax(new Printer ("6")); + fix = fax; + if (!expect (ACE_TEXT ("fix"), fix, false, 6, 1)) + ++errors; + if (!expect (ACE_TEXT ("fax"), fax, false, 6, 1)) + ++errors; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test reset to null\n"))); + Printer_Ptr fey(new Printer ("7")); + Printer_Ptr flo = fey; + flo.reset (); + if (!expect (ACE_TEXT ("fey"), fey, false, 7, 0)) + ++errors; + if (!expect (ACE_TEXT ("flo"), flo, true, 0, 0)) + ++errors; + + return errors; +} + +static int test_operator(void) +{ + int errors = 0; + + // test null + Printer_Ptr printer_null; + if (!printer_null) + { + } + else + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("!printer_null should be false\n"))); + } + if (printer_null) + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("printer_null should be false\n"))); + } + else + { + } + + // test not null + Printer_Ptr printer_not_null(new Printer("check not null")); + if (!printer_not_null) + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("!printer_not_null should be false\n"))); + } + else + { + } + if (printer_not_null) + { + } + else + { + ++errors; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("printer_not_null should be false\n"))); + } + + return errors; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Refcounted_Auto_Ptr_Test")); + + int test_errors = 0; + + // ========================================================================= + // The following test uses the ACE_Refcounted_Auto_Ptr in a single + // thread of control, hence we use the ACE_Null_Mutex + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) performing synchronous tests...\n"))); + + test_errors += test_reset_release (); + + Printer *printer1; + ACE_NEW_RETURN (printer1, + Printer ("I am printer 1"), + -1); + { + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r(printer1); + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r1(r); + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r2(r); + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r3(r); + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r4(r); + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r5 = r2; + ACE_Refcounted_Auto_Ptr<Printer, ACE_Null_Mutex> r6 = r1; + } + if (Printer::instance_count_ == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Printer instance count is 0; correct\n"))); + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Printer instance count %d; expecting 0\n"), + Printer::instance_count_.value ())); + ++test_errors; + } + +#if defined (ACE_HAS_THREADS) + + // ========================================================================= + // The following test uses the ACE_Refcounted_Auto_Ptr in multiple + // threads of control. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) performing asynchronous test...\n"))); + + Scheduler *scheduler_ptr; + + // Create active objects.. + ACE_NEW_RETURN (scheduler_ptr, + Scheduler (), + -1); + + auto_ptr<Scheduler> scheduler(scheduler_ptr); + + if (scheduler->open () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Error opening scheduler")), + 1); + + { + ACE_NEW_RETURN (printer1, + Printer ("I am printer 2"), + -1); + + Printer_var r (printer1); + + for (int i = 0; i < n_loops; i++) + // Spawn off the methods, which run in a separate thread as + // active object invocations. + scheduler->print (r); + } + + // Close things down. + scheduler->end (); + + scheduler->wait (); + + if (Printer::instance_count_ == 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Printer instance count is 0; correct\n"))); + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Printer instance count %d; expecting 0\n"), + Printer::instance_count_.value ())); + ++test_errors; + } + +#endif /* ACE_HAS_THREADS */ + + test_errors += test_operator(); + + ACE_END_TEST; + + return test_errors; +} diff --git a/ACE/tests/Refcounted_Auto_Ptr_Test.h b/ACE/tests/Refcounted_Auto_Ptr_Test.h new file mode 100644 index 00000000000..745b7239c3d --- /dev/null +++ b/ACE/tests/Refcounted_Auto_Ptr_Test.h @@ -0,0 +1,39 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Refcounted_Auto_Ptr_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_REFCOUNTED_AUTO_PTR_TEST_H +#define ACE_TESTS_REFCOUNTED_AUTO_PTR_TEST_H + +#include "ace/Atomic_Op.h" +#include "ace/Synch.h" + +struct Printer +{ + Printer (const char *message); + ~Printer (void) ; + + void print (void); + + const char *message_; + unsigned int which_; + static ACE_Atomic_Op<ACE_SYNCH_MUTEX, unsigned int> current_instance_; + static ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> instance_count_; +}; + +#endif /* ACE_TESTS_REFCOUNTED_AUTO_PTR_TEST_H */ diff --git a/ACE/tests/Reference_Counted_Event_Handler_Test.cpp b/ACE/tests/Reference_Counted_Event_Handler_Test.cpp new file mode 100644 index 00000000000..4857e647be9 --- /dev/null +++ b/ACE/tests/Reference_Counted_Event_Handler_Test.cpp @@ -0,0 +1,1016 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reference_Counted_Event_Handler_Test.cpp +// +// = DESCRIPTION +// This test is used to check reference counting of the Event +// Handler when it interacts with the Reactor. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Get_Opt.h" +#include "ace/ACE.h" + +ACE_RCSID(tests, Reference_Counted_Event_Handler_Test, "$Id$") + +static const char message[] = "abcdefghijklmnopqrstuvwxyz"; +static const int message_size = 26; +static int test_select_reactor = 1; +static int test_tp_reactor = 1; +static int test_wfmo_reactor = 1; +static int test_io = 1; +static int test_timers = 1; +static int test_find = 1; +static int test_simple_event_handler = 1; +static int test_reference_counted_event_handler_1 = 1; +static int test_reference_counted_event_handler_2 = 1; +static int test_closed_in_upcall_event_handler = 1; +static int debug = 1; +static const char *one_second_timeout = "one second timeout"; +static const char *two_second_timeout = "two second timeout"; + +class Reference_Counted_Event_Handler : public ACE_Event_Handler +{ +public: + + Reference_Counted_Event_Handler (int &events); + + ~Reference_Counted_Event_Handler (void); + + int handle_input (ACE_HANDLE); + + int handle_output (ACE_HANDLE); + + int handle_timeout (const ACE_Time_Value &, + const void *); + + int handle_signal (int, siginfo_t *, ucontext_t *); + + int handle_close (ACE_HANDLE, + ACE_Reactor_Mask); + + ACE_Event_Handler::Reference_Count add_reference (void); + + ACE_Event_Handler::Reference_Count remove_reference (void); + + ACE_Pipe pipe_; + + int &events_; + +}; + +Reference_Counted_Event_Handler::Reference_Counted_Event_Handler (int &events) + : events_ (events) +{ + int result = + this->pipe_.open (); + + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + ACE_DEBUG ((LM_DEBUG, + "Reference count in Reference_Counted_Event_Handler() is %d\n", + this->reference_count_.value ())); +} + +Reference_Counted_Event_Handler::~Reference_Counted_Event_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + "Reference count in ~Reference_Counted_Event_Handler() is %d\n", + this->reference_count_.value ())); + + this->pipe_.close (); +} + +int +Reference_Counted_Event_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "Reference count in Reference_Counted_Event_Handler::handle_input() is %d\n", + this->reference_count_.value ())); + + --this->events_; + + char buf[message_size + 1]; + + ssize_t result = + ACE::recv_n (this->pipe_.read_handle (), + buf, + sizeof buf - 1); + + ACE_ASSERT (result == message_size); + + buf[message_size] = '\0'; + + ACE_DEBUG ((LM_DEBUG, + "Message received: %C\n", + buf)); + + return 0; +} + +int +Reference_Counted_Event_Handler::handle_output (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "Reference count in Reference_Counted_Event_Handler::handle_output() is %d\n", + this->reference_count_.value ())); + + --this->events_; + + ssize_t result = + ACE::send_n (this->pipe_.write_handle (), + message, + message_size); + + ACE_ASSERT (result == message_size); + + // No longer interested in output. + return -1; +} + +int +Reference_Counted_Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *arg) +{ + ACE_DEBUG ((LM_DEBUG, + "Reference count in Reference_Counted_Event_Handler::handle_timeout() for arg = %C is %d\n", + (const char *) arg, + this->reference_count_.value ())); + + --this->events_; + + return 0; +} + +int +Reference_Counted_Event_Handler::handle_signal (int, + siginfo_t *, + ucontext_t *) +{ + return 0; +} + +int +Reference_Counted_Event_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask masks) +{ + ACE_DEBUG ((LM_DEBUG, + "Reference_Counted_Event_Handler::handle_close() called with handle = %d and masks = %d. " + "Reference count is %d\n", + handle, + masks, + this->reference_count_.value ())); + + return 0; +} + +ACE_Event_Handler::Reference_Count +Reference_Counted_Event_Handler::add_reference (void) +{ + ACE_Event_Handler::Reference_Count reference_count = + this->ACE_Event_Handler::add_reference (); + + ACE_DEBUG ((LM_DEBUG, + "Reference count after add_reference() is %d\n", + this->reference_count_.value ())); + + return reference_count; +} + +ACE_Event_Handler::Reference_Count +Reference_Counted_Event_Handler::remove_reference (void) +{ + ACE_Event_Handler::Reference_Count reference_count = + this->ACE_Event_Handler::remove_reference (); + + ACE_DEBUG ((LM_DEBUG, + "Reference count after remove_reference() is %d\n", + reference_count)); + + return reference_count; +} + +void +reference_counted_event_handler_test_1 (ACE_Reactor *reactor) +{ + int events = 0; + int result = 0; + + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (events); + + ACE_Event_Handler_var safe_handler (handler); + + if (test_io) + { + result = + reactor->register_handler (handler->pipe_.read_handle (), + handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + result = + reactor->register_handler (handler->pipe_.write_handle (), + handler, + ACE_Event_Handler::WRITE_MASK); + ACE_ASSERT (result == 0); + + events += 2; + } + + if (test_timers) + { + ACE_Time_Value const one_second (1); + long timer_id = + reactor->schedule_timer (handler, + one_second_timeout, + one_second, + one_second); + ACE_ASSERT (timer_id != -1); + + result = + reactor->cancel_timer (timer_id, + 0, + 0); + ACE_ASSERT (result == 1); + + timer_id = + reactor->schedule_timer (handler, + one_second_timeout, + one_second, + one_second); + ACE_ASSERT (timer_id != -1); + + ACE_Time_Value const two_second (2); + timer_id = + reactor->schedule_timer (handler, + two_second_timeout, + two_second); + ACE_ASSERT (result != -1); + + events += 3; + } + + while (events > 0) + { + result = + reactor->handle_events (); + } +} + +void +reference_counted_event_handler_test_2 (ACE_Reactor *reactor) +{ + int events = 0; + int result = 0; + ACE_Time_Value const one_second (1); + + if (test_find) + { + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (events); + + ACE_Event_Handler_var safe_handler (handler); + + result = + reactor->register_handler (handler->pipe_.read_handle (), + handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + { + ACE_Event_Handler *result_handler = 0; + + result = + reactor->handler (handler->pipe_.read_handle (), + ACE_Event_Handler::READ_MASK, + &result_handler); + ACE_Event_Handler_var safe_result_handler (result_handler); + + ACE_ASSERT (result == 0); + ACE_ASSERT (result_handler == handler); + } + + { + ACE_Event_Handler *result_handler = 0; + + result = + reactor->handler (handler->pipe_.read_handle (), + ACE_Event_Handler::WRITE_MASK, + &result_handler); + ACE_Event_Handler_var safe_result_handler (result_handler); + + ACE_ASSERT (result == -1); + } + + { + ACE_Event_Handler_var result_handler = + reactor->find_handler (handler->pipe_.read_handle ()); + + ACE_ASSERT (result_handler.handler () == handler); + } + } + + if (test_io) + { + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (events); + + ACE_Event_Handler_var safe_handler (handler); + + result = + reactor->register_handler (handler->pipe_.read_handle (), + handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + result = + reactor->register_handler (handler->pipe_.write_handle (), + handler, + ACE_Event_Handler::WRITE_MASK); + ACE_ASSERT (result == 0); + + events += 2; + } + + if (test_timers) + { + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (events); + + ACE_Event_Handler_var safe_handler (handler); + + long timer_id = + reactor->schedule_timer (handler, + one_second_timeout, + one_second, + one_second); + ACE_ASSERT (timer_id != -1); + + result = + reactor->cancel_timer (timer_id, + 0, + 0); + ACE_ASSERT (result == 1); + } + + if (test_timers) + { + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (events); + + ACE_Event_Handler_var safe_handler (handler); + + long timer_id = + reactor->schedule_timer (handler, + one_second_timeout, + one_second, + one_second); + ACE_ASSERT (timer_id != -1); + + ACE_Time_Value const two_second (2); + timer_id = + reactor->schedule_timer (handler, + two_second_timeout, + two_second); + ACE_ASSERT (result != -1); + + events += 3; + } + + while (events > 0) + { + result = + reactor->handle_events (); + } +} + +void +reference_count_1 (ACE_Reactor_Impl *impl) +{ + ACE_Reactor reactor (impl, 1); + + ACE_DEBUG ((LM_DEBUG, + "\nTesting Reference Counted Event Handler Test 1....\n\n")); + + reference_counted_event_handler_test_1 (&reactor); +} + +void +reference_count_2 (ACE_Reactor_Impl *impl) +{ + ACE_Reactor reactor (impl, 1); + + ACE_DEBUG ((LM_DEBUG, + "\nTesting Reference Counted Event Handler Test 2....\n\n")); + + reference_counted_event_handler_test_2 (&reactor); +} + +class Simple_Event_Handler : public ACE_Event_Handler +{ +public: + + Simple_Event_Handler (int &events, + int close_count); + + ~Simple_Event_Handler (void); + + int handle_input (ACE_HANDLE); + + int handle_output (ACE_HANDLE); + + int handle_timeout (const ACE_Time_Value &, + const void *); + + int handle_signal (int, siginfo_t *, ucontext_t *); + + int handle_close (ACE_HANDLE, + ACE_Reactor_Mask); + + ACE_Pipe pipe_; + + int &events_; + + int close_count_; + +}; + +Simple_Event_Handler::Simple_Event_Handler (int &events, + int close_count) + : events_ (events), + close_count_ (close_count) +{ + int result = + this->pipe_.open (); + + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler()\n")); +} + +Simple_Event_Handler::~Simple_Event_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + "~Simple_Event_Handler()\n")); + + this->pipe_.close (); +} + +int +Simple_Event_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler::handle_input()\n")); + + --this->events_; + + char buf[message_size + 1]; + + ssize_t result = + ACE::recv_n (this->pipe_.read_handle (), + buf, + sizeof buf - 1); + + ACE_ASSERT (result == message_size); + + buf[message_size] = '\0'; + + ACE_DEBUG ((LM_DEBUG, + "Message received: %C\n", + buf)); + + return 0; +} + +int +Simple_Event_Handler::handle_output (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler::handle_output()\n")); + + --this->events_; + + ssize_t result = + ACE::send_n (this->pipe_.write_handle (), + message, + message_size); + + ACE_ASSERT (result == message_size); + + // No longer interested in output. + return -1; +} + +int +Simple_Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *arg) +{ + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler::handle_timeout() for arg = %C\n", + (const char *) arg)); + + --this->events_; + + return 0; +} + +int +Simple_Event_Handler::handle_signal (int, + siginfo_t *, + ucontext_t *) +{ + return 0; +} + +int +Simple_Event_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask masks) +{ + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler::handle_close() called with handle = %d and masks = %d with close count = %d.\n", + handle, + masks, + --this->close_count_)); + + if (this->close_count_ == 0) + delete this; + + return 0; +} + +void +simple_event_handler (ACE_Reactor *reactor) +{ + int events = 0; + int result = 0; + ACE_Time_Value const one_second (1); + + if (test_find) + { + Simple_Event_Handler handler (events, + 0); + + result = + reactor->register_handler (handler.pipe_.read_handle (), + &handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + { + ACE_Event_Handler *result_handler = 0; + + result = + reactor->handler (handler.pipe_.read_handle (), + ACE_Event_Handler::READ_MASK, + &result_handler); + ACE_Event_Handler_var safe_result_handler (result_handler); + + ACE_ASSERT (result == 0); + ACE_ASSERT (result_handler == &handler); + } + + { + ACE_Event_Handler *result_handler = 0; + + result = + reactor->handler (handler.pipe_.read_handle (), + ACE_Event_Handler::WRITE_MASK, + &result_handler); + ACE_Event_Handler_var safe_result_handler (result_handler); + + ACE_ASSERT (result == -1); + } + + { + ACE_Event_Handler_var result_handler = + reactor->find_handler (handler.pipe_.read_handle ()); + + ACE_ASSERT (result_handler.handler () == &handler); + } + + result = + reactor->remove_handler (handler.pipe_.read_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK | ACE_Event_Handler::DONT_CALL); + + ACE_ASSERT (result == 0); + } + + if (test_io) + { + Simple_Event_Handler *handler = + new Simple_Event_Handler (events, + 2); + + result = + reactor->register_handler (handler->pipe_.read_handle (), + handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + result = + reactor->register_handler (handler->pipe_.write_handle (), + handler, + ACE_Event_Handler::WRITE_MASK); + ACE_ASSERT (result == 0); + + events += 2; + } + + if (test_timers) + { + Simple_Event_Handler *handler = + new Simple_Event_Handler (events, + 1); + + long timer_id = + reactor->schedule_timer (handler, + one_second_timeout, + one_second, + one_second); + ACE_ASSERT (timer_id != -1); + + result = + reactor->cancel_timer (timer_id, + 0, + 0); + ACE_ASSERT (result == 1); + } + + if (test_timers) + { + Simple_Event_Handler *handler = + new Simple_Event_Handler (events, + 1); + + long timer_id = + reactor->schedule_timer (handler, + one_second_timeout, + one_second, + one_second); + ACE_ASSERT (timer_id != -1); + + ACE_Time_Value const two_second (2); + timer_id = + reactor->schedule_timer (handler, + two_second_timeout, + two_second); + ACE_ASSERT (result != -1); + + events += 3; + } + + while (events > 0) + { + result = + reactor->handle_events (); + } +} + +void +simple (ACE_Reactor_Impl *impl) +{ + ACE_Reactor reactor (impl, 1); + + ACE_DEBUG ((LM_DEBUG, + "\nTesting Simple Event Handler....\n\n")); + + simple_event_handler (&reactor); +} + +class Closed_In_Upcall_Event_Handler : public ACE_Event_Handler +{ +public: + + Closed_In_Upcall_Event_Handler (int &events); + + ~Closed_In_Upcall_Event_Handler (void); + + int handle_input (ACE_HANDLE); + + int handle_close (ACE_HANDLE, + ACE_Reactor_Mask); + + ACE_Event_Handler::Reference_Count add_reference (void); + + ACE_Event_Handler::Reference_Count remove_reference (void); + + ACE_Pipe pipe_; + + int &events_; + +}; + +Closed_In_Upcall_Event_Handler::Closed_In_Upcall_Event_Handler (int &events) + : events_ (events) +{ + int result = + this->pipe_.open (); + + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + ACE_DEBUG ((LM_DEBUG, + "Closed_In_Upcall_Event_Handler()\n")); +} + +Closed_In_Upcall_Event_Handler::~Closed_In_Upcall_Event_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + "~Closed_In_Upcall_Event_Handler()\n")); + + this->pipe_.close (); +} + +int +Closed_In_Upcall_Event_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "Closed_In_Upcall_Event_Handler::handle_input()\n")); + + this->events_--; + + int result = + this->reactor ()->remove_handler (this->pipe_.read_handle (), + ACE_Event_Handler::ALL_EVENTS_MASK); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + char buf[message_size + 1]; + + ssize_t recv_result = + ACE::recv_n (this->pipe_.read_handle (), + buf, + sizeof buf - 1); + + ACE_ASSERT (recv_result == message_size); + + buf[message_size] = '\0'; + + ACE_DEBUG ((LM_DEBUG, + "Message received: %C\n", + buf)); + + return 0; +} + +int +Closed_In_Upcall_Event_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask masks) +{ + ACE_DEBUG ((LM_DEBUG, + "Closed_In_Upcall_Event_Handler::handle_close() called with handle = %d and masks = %d. " + "Reference count is %d\n", + handle, + masks, + this->reference_count_.value ())); + + return 0; +} + +ACE_Event_Handler::Reference_Count +Closed_In_Upcall_Event_Handler::add_reference (void) +{ + ACE_Event_Handler::Reference_Count reference_count = + this->ACE_Event_Handler::add_reference (); + + ACE_DEBUG ((LM_DEBUG, + "Reference count after add_reference() is %d\n", + this->reference_count_.value ())); + + return reference_count; +} + +ACE_Event_Handler::Reference_Count +Closed_In_Upcall_Event_Handler::remove_reference (void) +{ + ACE_Event_Handler::Reference_Count reference_count = + this->ACE_Event_Handler::remove_reference (); + + ACE_DEBUG ((LM_DEBUG, + "Reference count after remove_reference() is %d\n", + reference_count)); + + return reference_count; +} + +void +closed_in_upcall_event_handler (ACE_Reactor *reactor) +{ + int events = 0; + int handle_events_result = 0; + + if (test_io) + { + Closed_In_Upcall_Event_Handler *handler = 0; + ACE_NEW (handler, Closed_In_Upcall_Event_Handler (events)); + + ACE_Event_Handler_var safe_handler (handler); + + ssize_t send_n_result = + ACE::send_n (handler->pipe_.write_handle (), + message, + message_size); + + ACE_ASSERT (send_n_result == message_size); + + int register_handler_result = + reactor->register_handler (handler->pipe_.read_handle (), + handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (register_handler_result == 0); + + events += 1; + } + + while (events > 0) + { + handle_events_result = + reactor->handle_events (); + } +} + +void +closed_in_upcall (ACE_Reactor_Impl *impl) +{ + ACE_Reactor reactor (impl, 1); + + ACE_DEBUG ((LM_DEBUG, + "\nTesting Closed in Upcall Event Handler....\n\n")); + + closed_in_upcall_event_handler (&reactor); +} + +template <class REACTOR_IMPLEMENTATION> +class test +{ +public: + test (void); +}; + +template <class REACTOR_IMPLEMENTATION> +test<REACTOR_IMPLEMENTATION>::test (void) +{ + if (test_simple_event_handler) + simple (new REACTOR_IMPLEMENTATION); + + if (test_reference_counted_event_handler_1) + reference_count_1 (new REACTOR_IMPLEMENTATION); + + if (test_reference_counted_event_handler_2) + reference_count_2 (new REACTOR_IMPLEMENTATION); + + if (test_closed_in_upcall_event_handler) + closed_in_upcall (new REACTOR_IMPLEMENTATION); +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:b:c:f:g:h:i:k:l:m:z:")); + + int cc; + while ((cc = get_opt ()) != -1) + { + switch (cc) + { + case 'a': + test_select_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + test_tp_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + test_wfmo_reactor = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'f': + test_simple_event_handler = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'g': + test_reference_counted_event_handler_1 = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': + test_reference_counted_event_handler_2 = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + test_closed_in_upcall_event_handler = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'k': + test_io = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'l': + test_timers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'm': + test_find = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'z': + debug = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case '?': + case 'u': + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\nusage: %s \n\n") + ACE_TEXT ("\t[-a test Select Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-b test TP Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-c test WFMO Reactor] (defaults to %d)\n") + ACE_TEXT ("\t[-f test simple event handler] (defaults to %d)\n") + ACE_TEXT ("\t[-g test reference counted event handler (first test)] (defaults to %d)\n") + ACE_TEXT ("\t[-h test reference counted event handler (second test)] (defaults to %d)\n") + ACE_TEXT ("\t[-i test closed in upcall event handler] (defaults to %d)\n") + ACE_TEXT ("\t[-k test io] (defaults to %d)\n") + ACE_TEXT ("\t[-l test timers] (defaults to %d)\n") + ACE_TEXT ("\t[-m test find] (defaults to %d)\n") + ACE_TEXT ("\t[-z debug] (defaults to %d)\n") + ACE_TEXT ("\n"), + argv[0], + test_select_reactor, + test_tp_reactor, + test_wfmo_reactor, + test_simple_event_handler, + test_reference_counted_event_handler_1, + test_reference_counted_event_handler_2, + test_closed_in_upcall_event_handler, + test_io, + test_timers, + test_find, + debug)); + return -1; + } + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Reference_Counted_Event_Handler_Test")); + + // Validate options. + int result = + parse_args (argc, argv); + if (result != 0) + return result; + + if (test_select_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting Select Reactor....\n\n")); + + test<ACE_Select_Reactor> test; + ACE_UNUSED_ARG (test); + } + + if (test_tp_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting TP Reactor....\n\n")); + + test<ACE_TP_Reactor> test; + ACE_UNUSED_ARG (test); + } + +#if defined (ACE_WIN32) && \ + (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 == 0)) + + if (test_wfmo_reactor) + { + ACE_DEBUG ((LM_DEBUG, + "\n\nTesting WFMO Reactor....\n\n")); + + test<ACE_WFMO_Reactor> test; + ACE_UNUSED_ARG (test); + } + +#endif /* ACE_WIN32 && ACE_HAS_WINSOCK2 */ + + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/Reverse_Lock_Test.cpp b/ACE/tests/Reverse_Lock_Test.cpp new file mode 100644 index 00000000000..5ee08dafbee --- /dev/null +++ b/ACE/tests/Reverse_Lock_Test.cpp @@ -0,0 +1,48 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Reverse_Lock_Test.cpp +// +// = DESCRIPTION +// This is a simple test to illustrate the functionality of +// ACE_Reverse_Lock. The test acquires and releases mutexes. No +// command line arguments are needed to run the test. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Synch_Traits.h" +#include "ace/Thread_Mutex.h" +#include "ace/Guard_T.h" +#include "ace/Reverse_Lock_T.h" + +ACE_RCSID(tests, Reverse_Lock_Test, "$Id$") + +typedef ACE_Reverse_Lock<ACE_SYNCH_MUTEX> REVERSE_MUTEX; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reverse_Lock_Test")); + + ACE_SYNCH_MUTEX mutex; + REVERSE_MUTEX reverse_mutex (mutex); + + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, monitor, mutex, -1); + + ACE_GUARD_RETURN (REVERSE_MUTEX, reverse_monitor, reverse_mutex, -1); + } + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/SOCK_Connector_Test.cpp b/ACE/tests/SOCK_Connector_Test.cpp new file mode 100644 index 00000000000..fe9039bd31f --- /dev/null +++ b/ACE/tests/SOCK_Connector_Test.cpp @@ -0,0 +1,320 @@ +// $Id$ + +// ========================================================================== +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_Connector_Test.cpp +// +// = DESCRIPTION +// This is a test of ACE_SOCK_Connector, focusing on failure cases more +// than on success cases. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ========================================================================== + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/Time_Value.h" +#include "ace/SOCK_Stream.h" +#include "ace/OS_NS_sys_utsname.h" +#include "ace/OS_NS_netdb.h" + +ACE_RCSID(tests, SOCK_Connector_Test, "$Id$") + +// Host candidate list +struct Host_Candidate +{ + ACE_TCHAR host_name[MAXHOSTNAMELEN]; +}; + +const int MAX_CANDIDATES = 50; +Host_Candidate candidate[MAX_CANDIDATES]; + +#if !defined (ACE_LACKS_GETHOSTENT) +// Determine if a host exists, is reachable, and is up. Attempt a +// blocking connection to it; if it succeeds, then the host exists, is +// reachable, and is up. + +static u_int +host_is_up (ACE_TCHAR hostname[]) +{ + ACE_SOCK_Connector con; + ACE_SOCK_Stream sock; + + // The ACE_INET_Addr construction causes gethostbyname_r to be + // called, so we need to copy the hostname. + ACE_TCHAR test_host[MAXHOSTNAMELEN]; + ACE_OS::strcpy (test_host, hostname); + + ACE_INET_Addr another_host ((u_short) 7, test_host); + ACE_Time_Value timeout_value (5); + const int status = con.connect (sock, + another_host, + &timeout_value); + sock.close (); + return status == 0 ? 1 : 0; +} +#endif /* ! ACE_LACKS_GETHOSTENT */ + +// The original problem this program tested for was incorrectly saying +// a non-blocking connect completed successfully when it didn't. The +// test doesn't always work when done to localhost +// (platform-dependant) so we look around for another host - any other +// one will do. + +static void +find_another_host (ACE_TCHAR other_host[]) +{ + static ACE_TCHAR cached_other_host[MAXHOSTNAMELEN] = {'\0'}; + + if (cached_other_host[0] == '\0') + { + + ACE_OS::strcpy (other_host, + ACE_DEFAULT_SERVER_HOST); // If all else fails + +#if !defined (ACE_LACKS_GETHOSTENT) + // These gethost-type things don't work everywhere. + struct hostent *h; + ACE_utsname un; + + ACE_OS::uname (&un); + + h = ACE_OS::gethostbyname (un.nodename); + + if (h == 0) + ACE_OS::strcpy (other_host, ACE_LOCALHOST); + else + // Use me if can't find another + ACE_OS::strcpy (other_host, ACE_TEXT_CHAR_TO_TCHAR (h->h_name)); + + // @@ We really need to add wrappers for these hostent methods. + + // Optimize for sequential access of DNS or hosts file. + sethostent (1); + + int candidate_count = 0; + + // Accumulate candidates first. There is some interaction on + // Linux systems between <gethostent> and <gethostbyname_r> + // (called by ACE_INET_Addr in host_is_up) This otherwise causes + // an infinite loop on Linux --mas 03-08-2001 + while ((h = gethostent ()) != 0) + { + if (ACE_OS::strcmp (h->h_name, + ACE_TEXT_ALWAYS_CHAR (ACE_DEFAULT_SERVER_HOST)) == 0) + continue; + // AIX just _has_ to be different + if (ACE_OS::strcmp (h->h_name, "loopback") == 0) + continue; + + // If not me. + if (ACE_OS::strcmp + (h->h_name, ACE_TEXT_ALWAYS_CHAR (other_host)) != 0 + && ACE_OS::strcmp (h->h_name, un.nodename) != 0) + { + ACE_OS::strcpy (candidate[candidate_count].host_name, + ACE_TEXT_CHAR_TO_TCHAR (h->h_name)); + if (++candidate_count >= MAX_CANDIDATES) + break; + } + } + + // Now try to connect to candidates + for (int i = 0; i < candidate_count; i++) + if (host_is_up (candidate[i].host_name)) + { + ACE_OS::strcpy (other_host, candidate[i].host_name); + break; + } + + sethostent (0); + endhostent (); +#endif /* ! ACE_LACKS_GETHOSTENT */ + + ACE_OS::strcpy (cached_other_host, other_host); + } + else + ACE_OS::strcpy (other_host, cached_other_host); +} + +static int +fail_no_listener_nonblocking (void) +{ + ACE_TCHAR test_host[MAXHOSTNAMELEN], test_addr[MAXHOSTNAMELEN + 8]; + int status; + ACE_INET_Addr nobody_home; + ACE_SOCK_Connector con; + ACE_SOCK_Stream sock; + ACE_Time_Value nonblock (0, 0); + + find_another_host (test_host); + if (nobody_home.set ((u_short) 42000, test_host) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Host lookup for %s %p\n"), + test_host, + ACE_TEXT ("failed"))); + return -1; + } + nobody_home.addr_to_string (test_addr, MAXHOSTNAMELEN + 8); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing to host \"%s\" (%s)\n"), + test_host, test_addr)); + + status = con.connect (sock, nobody_home, &nonblock); + + // Need a port that will fail. + if (status == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Connect which should fail didn't\n"))); + status = -1; + } + + // On some systems, a failed connect to localhost will return + // ECONNREFUSED or ENETUNREACH directly, instead of + // EWOULDBLOCK. That is also fine. + + else if (errno == EWOULDBLOCK || + errno == ECONNREFUSED || + errno == ENETUNREACH) + { + if (sock.get_handle () != ACE_INVALID_HANDLE) + status = con.complete (sock); + + if (status != -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Connect which should fail didn't\n"))); + status = -1; + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Proper fail"))); + status = 0; + } + } + else + { + ACE_DEBUG ((LM_WARNING, + ACE_TEXT ("Test not executed fully; ") + ACE_TEXT ("expected EWOULDBLOCK, %p (%d)\n"), + ACE_TEXT ("not"), errno)); + status = -1; + } + + // Just in case. + sock.close (); + + return status; +} + + +// This test tries to hit a port that's listening. Echo (7) is pretty +// popular. Just in case, though, it won't report a failure if it +// gets "refused" (no listener) since the real fixed bug this is +// testing is a returned error of EWOULDBLOCK when the connect really +// did work. That was a side-affect of how +// <ACE::handle_timed_complete> does checks on some systems. + +static int +succeed_nonblocking (void) +{ + ACE_TCHAR test_host[MAXHOSTNAMELEN], test_addr[MAXHOSTNAMELEN + 8]; + int status; + ACE_INET_Addr echo_server; + ACE_SOCK_Connector con; + ACE_SOCK_Stream sock; + ACE_Time_Value nonblock (0, 0); + u_short test_port = 7; // Echo + + find_another_host (test_host); + if (ACE_OS::strcmp (ACE_TEXT ("localhost"), test_host) == 0) + { +#if defined (ACE_WIN32) + test_port = 80; // Echo not available on Win32; try web server +#endif /* ACE_WIN32 */ + } + if (echo_server.set (test_port, test_host) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Host lookup for %s %p\n"), + test_host, + ACE_TEXT ("failed"))); + return -1; + } + echo_server.addr_to_string (test_addr, MAXHOSTNAMELEN + 8); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Testing to host \"%s\", port %d (%s)\n"), + test_host, test_port, test_addr)); + status = con.connect (sock, echo_server, &nonblock); + + // Need to test the call to 'complete' really. + if (status == 0 || (status == -1 && errno != EWOULDBLOCK)) + { + ACE_DEBUG((LM_WARNING, + ACE_TEXT ("Immediate success/fail; test not completed\n"))); + status = 0; + } + else + { + if (sock.get_handle () != ACE_INVALID_HANDLE) + status = con.complete (sock); + + if (status == -1) + { + // Reset the status _before_ doing the printout, in case the + // printout overwrites errno. + if (errno == ECONNREFUSED) + { + status = 0; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Should succeed, but refused: ok\n"))); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("%p\n"), + ACE_TEXT("connect should succeed, but"))); + } + } + else + ACE_DEBUG((LM_DEBUG, + ACE_TEXT("Connect which should succeed, did\n"))); + } + + // Just in case. + sock.close (); + + return status; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Connector_Test")); + + int status = 0; + + if (fail_no_listener_nonblocking () == -1) + status = 1; + + if (succeed_nonblocking () == -1) + status = 1; + + ACE_END_TEST; + return status; +} + + + diff --git a/ACE/tests/SOCK_Dgram_Bcast_Test.cpp b/ACE/tests/SOCK_Dgram_Bcast_Test.cpp new file mode 100644 index 00000000000..531bf196151 --- /dev/null +++ b/ACE/tests/SOCK_Dgram_Bcast_Test.cpp @@ -0,0 +1,261 @@ +// $Id$ +// =========================================================================== +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_Dgram_Bcast_Test.cpp +// +// = DESCRIPTION +// This simple broadcast test is intended to check if ACE is capable +// of sending and receiving broadcasts. In single host environment most +// errors related to invalid broadcast initialization will not manifest +// themself, because usually broadcast on localhost interface works +// correctly. For this reason one should run also this test on two distinct +// hosts in single LAN. +// Tests that a call to open with an any address binds to the any address +// for the protocol passed in. +// +// = AUTHOR +// Marek Brudka (mbrudka@elka.pw.edu.pl) +// +// ========================================================================== + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Log_Msg.h" +#include "ace/Get_Opt.h" +#include "ace/SOCK_Dgram_Bcast.h" +#include "ace/Thread_Manager.h" +#include "ace/Process.h" +#include "ace/Process_Manager.h" + +static int dgram_port = 14521; +static int dgrams_no = 10; +static ACE_Time_Value dgram_recv_timeout( 5, 0 ); +static ACE_exitcode receiver_exit_code = 0; + +/*\brief Create and send single datagram + \param socket broadcast over this socket + \param datagram_no datagram identifier + \return -1 if error, 0 if OK +*/ +int send_datagram (ACE_SOCK_Dgram_Bcast &socket, int datagram_no) +{ + static char dgram_buffer[BUFSIZ]; + + ACE_OS::snprintf (dgram_buffer, sizeof(dgram_buffer), + "Datagram %d", datagram_no); + if (socket.send (dgram_buffer, + ACE_OS::strlen (dgram_buffer) + 1, + dgram_port) < 0 ) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Cannot broadcast datagram")), -1); + else + ACE_DEBUG ((LM_INFO, ACE_TEXT ("%C sent\n"), dgram_buffer)); + return 0; +} + +/*\brief Send a sequence of datagrams with 1 second period + \note Th function employs dgram_port and dgrams_no global variables + \retval -1 if error + \retval 0 if sent +*/ +int run_sender( ) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Sending %d datagrams on port %d\n"), + dgrams_no, + dgram_port)); + ACE_SOCK_Dgram_Bcast socket; + + if (socket.open (ACE_Addr::sap_any) != -1) + { + while (dgrams_no-- != 0) + { + if (send_datagram (socket, dgrams_no) < 0) + break; + ACE_OS::sleep (1); + } + socket.close (); + return (0); + } + + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Cannot open broadcast socket")), -1); +} + +/*\brief Receive single datagram + \note The function employes dgram_port and dgram_recv_timeout variables + \retval -1 if not received, + \retval 0 received a datagrams +*/ +int run_receiver () +{ + ACE_DEBUG + ((LM_INFO, + ACE_TEXT ("Receiving datagrams from port %d with timeout %d ms\n"), + dgram_port, dgram_recv_timeout.msec ())); + + ACE_SOCK_Dgram socket; + ACE_INET_Addr remote ; + static char dgram_buffer[BUFSIZ]; + + if (socket.open (ACE_INET_Addr (dgram_port)) != -1) + if (socket.recv (dgram_buffer, sizeof (dgram_buffer), + remote, 0, &dgram_recv_timeout) > 0) + { + ACE_DEBUG ((LM_INFO, ACE_TEXT ("%C received\n"), dgram_buffer)); + return 0; + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Cannot receive datagrams")), -1); + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p: %d\n"), + ACE_TEXT ("Cannot open broadcast socket on port"), dgram_port), -1); + } +} + +#if !defined (ACE_HAS_PROCESS_SPAWN) && defined (ACE_HAS_THREADS) +/* \brief Thread main function to run run_receiver function + \note run_receiver return valu is stored in receiver_exit_code global variable +*/ +static ACE_THR_FUNC_RETURN run_thread_receiver (void *) +{ + receiver_exit_code = run_receiver (); + return 0; +} +#endif /* !defined (ACE_HAS_PROCESS_SPAWN) && defined (ACE_HAS_THREADS) */ + +/* \brief Just runs automatic tests + +Function sends a number of datagrams, spawns child thread or process and +tries to receive at least one datagram. +\retval 0 datagram was received +\retval -1 datagram was not received +*/ +int run_auto_test (const ACE_TCHAR *prog_name) +{ +#if defined (ACE_HAS_PROCESS_SPAWN) + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Running auto_tests in process mode\n"))); + + ACE_Process_Options opts; + pid_t child_pid; + opts.command_line (ACE_TEXT ("%s -p %d -t %d -a -r"), + prog_name, dgram_port, dgram_recv_timeout.msec ()); + if ((child_pid = ACE_Process_Manager::instance ()->spawn (opts)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n()")), -1); + +#elif defined (ACE_HAS_THREADS) + ACE_UNUSED_ARG (prog_name); + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Running auto_tests in thread mode\n"))); + if (ACE_Thread_Manager::instance ()->spawn (run_thread_receiver) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n ()")), -1); +#else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Cannot run in auto_test mode without fork or threads.\n")), + -1); +#endif /* defined (ACE_HAS_PROCESS_SPAWN) */ + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Sending datagrams on port %d in auto_test mode\n"), + dgram_port)); + + ACE_SOCK_Dgram_Bcast socket; + + if (socket.open (ACE_Addr::sap_any) != -1) + { + // send datagrams until child finishes + while (1) + { + send_datagram (socket, dgrams_no--); + ACE_Time_Value child_timeout (1); +#if defined (ACE_HAS_PROCESS_SPAWN) + + if (ACE_Process_Manager::instance ()->wait (child_pid, + child_timeout, + &receiver_exit_code) == child_pid) + break; +#else /* ACE_HAS_THREADS */ + // sleep 1 second or wait for child thread + child_timeout += ACE_OS::gettimeofday () ; + if (ACE_Thread_Manager::instance ()->wait (&child_timeout) == 0) + break; +#endif + } + socket.close (); + ACE_DEBUG ((LM_INFO, ACE_TEXT ("Child finished with %d exit code\n"), + receiver_exit_code)); + } + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), + ACE_TEXT ("Cannot open broadcast socket")), -1); + return (receiver_exit_code); +} + +void print_usage (void) +{ + ACE_OS::printf("Usage:SOCK_Dgram_Bast_Test [-p port] [-n dgrams_no] [-t timeout_ms] [-s] [-r]\n" + "\tp broadcast port [14521]\n" + "\tn number of datagrams to broadcast [30] (<0 infinite)\n" + "\tt timeout in seconds for receive [5] (<=0 infinite)\n" + "\ts send datagrams and exit\n" + "\tr receive one datagram and exit\n\n" + "\t run auto-test when no r and s option is passed\n" + "\t test failures are minifested by -1 exit value, otherwise 0\n"); +} + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + // parse options and run in appropriate mode + int opt = 0; + int auto_test_recv = 0; + int result = 0; + ACE_Get_Opt opts (argc, argv, ACE_TEXT ("p:t:n:sra")); + while ((opt = opts ()) != -1) + switch (opt) + { + case 'a': + auto_test_recv = 1; + break; + case 's': + return (run_sender()); + case 'r': + { + if (auto_test_recv) + { + ACE_START_TEST (ACE_TEXT ("SOCK_Dgram_Bcast_Test_Child")); + result = run_receiver (); + ACE_END_TEST; + return result; + } + return (run_receiver ()); + } + case 'n': + dgrams_no = ACE_OS::atoi (opts.opt_arg ()); + break; + case 't': + dgram_recv_timeout.msec (ACE_OS::atoi (opts.opt_arg ())); + break; + case 'p': + dgram_port = ACE_OS::atoi (opts.opt_arg ()); + break; + default: + print_usage (); + return -1; + } + ACE_START_TEST (ACE_TEXT ("SOCK_Dgram_Bcast_Test")); + result = run_auto_test (ACE_TEXT ("SOCK_Dgram_Bcast_Test")); + ACE_END_TEST; + return result; +} diff --git a/ACE/tests/SOCK_Dgram_Test.cpp b/ACE/tests/SOCK_Dgram_Test.cpp new file mode 100644 index 00000000000..1447eb5283c --- /dev/null +++ b/ACE/tests/SOCK_Dgram_Test.cpp @@ -0,0 +1,308 @@ +// $Id$ +// =========================================================================== +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_Dgram.cpp +// +// = DESCRIPTION +// Tests that a call to open with an any address binds to the any address +// for the protocol passed in. +// +// This test uses the same test setup as SOCK_Test. +// +// = AUTHOR +// Brian Buesker (bbuesker@qualcomm.com) +// +// ========================================================================== + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/SOCK_Dgram.h" +#include "ace/Log_Msg.h" +#include "ace/Time_Value.h" +#include "ace/OS_NS_unistd.h" + +#define SERVER_PORT 20000 +#define TEST_DATA ACE_TEXT ("UDP Open Test") + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg; + ACE_INET_Addr server_addr, peer_addr; + ACE_SOCK_Dgram cli_dgram; + ACE_Time_Value timeout (1); + + ACE_TCHAR buf[20]; + ACE_TCHAR hostname_string[100]; + + if (remote_addr->get_type () == AF_INET) + { + server_addr.set (remote_addr->get_port_number (), + ACE_LOCALHOST, + 1, + remote_addr->get_type ()); + } +#if defined (ACE_HAS_IPV6) + else + { + server_addr.set (remote_addr->get_port_number(), + ACE_IPV6_LOCALHOST, + 1, + remote_addr->get_type ()); + } +#endif /* ACE_HAS_IPV6 */ + + server_addr.addr_to_string (hostname_string, 100); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting client UDP send to %s\n"), + hostname_string)); + + if (cli_dgram.open (ACE_Addr::sap_any, server_addr.get_type ()) == -1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("(%P|%t) protocol %d, %p\n"), + server_addr.get_type (), + ACE_TEXT ("SOCK_Dgram open"))); + } + else if (cli_dgram.send (TEST_DATA, sizeof (TEST_DATA), server_addr) == -1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("(%P|%t) UDP send to %s %p\n"), + hostname_string, + ACE_TEXT ("failed"))); + } + else + { + ssize_t rcv_cnt = cli_dgram.recv (buf, + sizeof (buf), + peer_addr, + 0, + &timeout); + if (rcv_cnt == -1) + { + if (errno == ETIME) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("(%P|%t) UDP recv on proto %d timed out\n"), + server_addr.get_type ())); + } + else + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("(%P|%t) UDP recv on proto %d %p\n"), + server_addr.get_type (), + ACE_TEXT("failed"))); + } + } + else + { + // recv() ok, check data and 'from' address + size_t rcv_siz = static_cast<size_t> (rcv_cnt); + if (rcv_siz != sizeof (TEST_DATA)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv cnt %B; should be %B\n"), + rcv_siz, + sizeof (TEST_DATA))); + } + else + { + buf[rcv_siz] = '\0'; + if (ACE_OS::strncmp (buf, TEST_DATA, ACE_OS::strlen (TEST_DATA))) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv string should be %s; ") + ACE_TEXT (" but is %s\n"), + TEST_DATA, buf)); + } + if (peer_addr.get_type () != server_addr.get_type ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv addr type %d; should be %d\n"), + peer_addr.get_type (), + server_addr.get_type ())); + if (peer_addr.get_size () != server_addr.get_size ()) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv addr size %d; should be %d\n"), + peer_addr.get_size (), + server_addr.get_size ())); + } + } + + cli_dgram.close(); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Dgram *server_dgram = (ACE_SOCK_Dgram *) arg; + ACE_INET_Addr peer_addr; + + ACE_TCHAR buf[20]; + ACE_TCHAR hostname_string[100]; + ssize_t rcv_cnt; + + if ((rcv_cnt = server_dgram->recv (buf, sizeof (buf), peer_addr, 0)) == -1) + { + ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) %p\n"), ACE_TEXT("server recv"))); + } + else + { + // recv() ok, check data and 'from' address + size_t rcv_siz = static_cast<size_t> (rcv_cnt); + if (rcv_siz != sizeof (TEST_DATA)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv cnt %B; should be %B\n"), + rcv_siz, + sizeof (TEST_DATA))); + } + else + { + buf[rcv_siz / sizeof (ACE_TCHAR)] = '\0'; + if (ACE_OS::strncmp (buf, TEST_DATA, ACE_OS::strlen (TEST_DATA))) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv string should be %s; ") + ACE_TEXT (" but is %s\n"), + TEST_DATA, buf)); + } + + if (0 == peer_addr.addr_to_string (hostname_string, + sizeof (hostname_string))) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Echoing data to %s\n"), + hostname_string)); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("addr_to_string"))); + if (server_dgram->send (TEST_DATA, sizeof (TEST_DATA), + peer_addr, 0) == -1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("(%P|%t) %p\n"), + ACE_TEXT("Server UDP send failed"))); + } + } + server_dgram->close (); + + return 0; +} + +static int +spawn (int proto) +{ + ACE_SOCK_Dgram server_dgram; + ACE_INET_Addr server_addr; + + if (proto == AF_INET) + { + server_addr.set (SERVER_PORT, ACE_LOCALHOST, 1, proto); + } +#if defined (ACE_HAS_IPV6) + else + { + server_addr.set (SERVER_PORT, ACE_IPV6_LOCALHOST, 1, proto); + } +#endif /* ACE_HAS_IPV6 */ + + // Bind UDP server to the appropriate port + if (server_dgram.open (server_addr, proto) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("server dgram open")), + 1); + } + else + { + if (server_addr.get_port_number() != SERVER_PORT) + { + ACE_TCHAR hostname_string[100]; + server_addr.addr_to_string (hostname_string, 100); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P|%t) Portnumber has unexpected value of %d on host %s\n"), + server_addr.get_port_number(), hostname_string), 1); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) started server at proto %d, port %d\n"), + proto, + server_addr.get_port_number ())); +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("fork failed"))); + /* NOTREACHED */ + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server ((void *) &server_dgram); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + (void *) &server_dgram, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("thread create failed")), + 1); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + (void *) &server_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("thread create failed")), + 1); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + } + + server_dgram.close (); + } + + return 0; +} + +int run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Dgram_Test")); + + int retval = spawn (AF_INET); + +#if defined (ACE_HAS_IPV6) + + if (retval == 0) + { + retval = spawn (AF_INET6); + } + +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + return retval; +} diff --git a/ACE/tests/SOCK_Netlink_Test.cpp b/ACE/tests/SOCK_Netlink_Test.cpp new file mode 100644 index 00000000000..f23412cdd52 --- /dev/null +++ b/ACE/tests/SOCK_Netlink_Test.cpp @@ -0,0 +1,980 @@ +// $Id$ + +// ====================================================================// +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_Netlink_Test.cpp +// +// = DESCRIPTION +// Tests adding of a secondary IP-address, using linux netlink +// sockets. +// +// = AUTHOR +// +// Robert Iakobashvili, coroberti@gmail.com +// +// ====================================================================// + +#include "test_config.h" + +#ifdef ACE_HAS_NETLINK + +#include "ace/Event_Handler.h" +#include "ace/Reactor.h" +#include "ace/Log_Msg.h" +#include "ace/Get_Opt.h" + +#include "ace/Netlink_Addr.h" +#include "ace/SOCK_Netlink.h" + +#include "ace/OS_NS_sys_socket.h" +#include "ace/OS_NS_time.h" + +#include <linux/rtnetlink.h> + + +/** + * NETLINK SOCKET INTERFACE is a socket API communication + * between linux kernel and userland. + * + * Main usage of netlink communication is for communication between + * kernel/custom modules/drivers and userspace; + * + * In order not to force ACE-tests runners to install some driver for testing + * purposes, we are using here a build-in netlink interface used by linux for + * "routing" purposes (rtnetlink). + * + * This test is hopefully a useful example of how via netlink to add a new + * secondary IPv4 address to an interface and to delete it further. The + * example is integrated within reactive framework for IO demultiplexing. + * + * A one button test adds a new secondary IP-address to a loopback + * interface and deletes it. Even if the address added remains, this shall no + * cause any damage. In any case the lifetime of a secondary ip is limited + * till the next reboot. You may wish to run the test with -d option (not to + * make an address cleanup after addtion) and see it by 'ip addr' command. + * Further re-run of the test without -d option will cleanup the address. + * + * Please, note, that it is ok that the message "rtnetlink error message: + * Cannot assign requested address" will appear on your console, because first + * the test is trying to remove the secondary-ip and errors, if it was not before. + * + * The one-button test fails, of there is no a loopback device named "lo" on a + * host or the device is disabled. + * + * The same rtnetlink interface may be used to get/add/delete ip-addresses, + * get/add/delete routing rules, control ARP entires, control Qdisk disciplines, + * traffic classes and traffic filters, manage network interface configuration + * + * For more information, please, read man pages: + * netlink (3), netlink (7), rtnetlink (3), rtnetlink (3), rtnetlink (7) and ip (8). + * + * Some ideas for the test were borrowed from the code of iprouted2 + * written by Alexey Kuznetsov. + * + * Command line options: + * + * -d do not cleanup the ip-address after addition (so that you can see it by the + * 'ip addr' command) + * + * -i the name of interface to add the address + * + * -a ipv4 slash netmask bits, like "192.168.1.1/24" + * + + From Linux headers: + + // Generic structure for encapsulation of optional route information. + // It is reminiscent of sockaddr, but with sa_family replaced with attribute type. + struct rtattr + { + unsigned short rta_len; + unsigned short rta_type; + }; + + //Interface address. + struct ifaddrmsg + { + unsigned char ifa_family; + unsigned char ifa_prefixlen; // The prefix length is the length of the address mask + unsigned char ifa_flags; // Flags: IFA_F_SECONDARY for secondary + // address (old alias interface), IFA_F_PERMANENT + // for a permanent address + unsigned char ifa_scope; // locality + int ifa_index; // Link index is the interface index in the table of interfaces. + }; + + struct nlmsghdr + { + __u32 nlmsg_len; // Length of message including header + __u16 nlmsg_type; // Message content + __u16 nlmsg_flags; // Additional flags + __u32 nlmsg_seq; // Sequence number + __u32 nlmsg_pid; // Sending process PID + }; +*/ + + +// The global config params +// +static int one_button_test = 0; +static char ip_slash_mask[32]; +static char net_dev_name[16]; +static int dont_cleanup_added_ip = 0; + +// The function returns index of an interface by its name +// +int +get_if_index (const char*const interface, + int &if_index) +{ + if_index = -1; + + struct ifreq if_req; + ACE_OS::memset (&if_req, 0, sizeof (struct ifreq)); + + ACE_OS::strncpy (if_req.ifr_name, + static_cast<const char*> (interface), + sizeof (if_req.ifr_name)); + + ACE_HANDLE s = ACE_OS::socket (AF_INET,SOCK_DGRAM,0); + + if (s == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_if_index - failed on\n") + ACE_TEXT ("ACE_OS::socket")), + -1); + + int result = ACE_OS::ioctl (s, + SIOCGIFINDEX, + reinterpret_cast<char*> (&if_req)); + + if (result == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_if_index:") + ACE_TEXT ("ioctl (get interface index)"))); + } + else + { + if_index = if_req.ifr_ifindex; + } + return result; +} + +/** + An assisting structure for passing data via routing netlink socket. + The idea borrowed from a great iprouted2 utility, + written by Alexey Kuznetsov. +*/ +struct Inet_Prefix +{ + u_char family; + u_char bytelen; + ACE_INT16 bitlen; + ACE_UINT32 data[4]; +}; + + +/** + * Contains header netlink message header of a type nlmsghdr with a + * following request type dependent controlling structure, which is for + * adding/deleting IP-addresses is of type ifaddrmsg with a following + * buffer, containing the data: ip-address, its length, number of netmask + * bits, etc. + */ +struct Netlink_Request +{ + struct nlmsghdr nhdr_; // message header + struct ifaddrmsg ifa_; // interface + char buf_[256]; +}; + + + +/** + * The handler first is trying to delete an ip-address, further + * to add the ip and, if successful to cleanup the address. + */ +class Secondary_Ipaddr_Handler : public ACE_Event_Handler +{ +public: + + // Default constructor + Secondary_Ipaddr_Handler (void); + + // Destructor + virtual ~Secondary_Ipaddr_Handler (void); + + //FUZZ: disable check_for_lack_ACE_OS + // Initialization. Schedules a timer to run start the business. + // + int open (ACE_Reactor *const reactor, + char* const ip_slash_mask, + const char *const if_name); + //FUZZ: enable check_for_lack_ACE_OS + + // Returns reference to netlink socket. Necessary for reactor. + virtual ACE_HANDLE get_handle (void) const; + + /** + * Takes care of the input. Reads the incoming messages, + * makes their processing. + */ + virtual int handle_input (ACE_HANDLE handle); + + // Makes clean-up + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + // Runs a state machine. Controls adding/deleting of ip-address. + int handle_timeout (ACE_Time_Value const & tv, + void const * arg = 0); + + // Sends to kernel a request to add secondary ip/mask to an + // interface. + int add_ip (char* const ip_slash_mask, + const char *const if_name); + + // Sends to kernel a request to delete secondary ip/mask + // from an interface. + int delete_ip (char* const ip_slash_mask, + const char *const if_name); + + /* + * 1. We are trying to delete the ip-address, if exists. + * Shall be either successful, or fail, when no-ip yet. + * 2. Adding the ip-address, shall be successful; + * 3. Cleaning up the ip-address. Shall be successful as well. + */ + enum + { + START = 0, + IP_DELETED, + IP_ADDED, + IP_CLEANUPED, + SUCCESS, + FAILED + }; + + // Returns the currect state + int get_state () const { return this->state_;} + +protected: + + //FUZZ: disable check_for_lack_ACE_OS + // De-registers the handler from the reactor, + // other cleanup jobs + virtual int close (); + + ACE_SOCK_Netlink& socket (); + //FUZZ: enable check_for_lack_ACE_OS + +private: + + // Schedule two sec timer. + int schedule_one_sec_timer (); + + // A workhorse for add_ip () and delete_ip () + int dispatch_ip_operation (char* const ip_slash_mask, + const char *const if_name, + bool action); + + /** + * Initializes netlink request for adding (action = true) or + * deleting (action = false) of a secondary ip-address/mask. + */ + int init_netlink_request (char* const ip_slash_mask, + const char *const if_name, + Netlink_Request& net_req, + bool action); + + // Fills data part of Netlink_Request + int fill_inet_prefix (Inet_Prefix &inet_prefix, + const char*const ip_slash_netmask); + + /** + * Fills routing request (operations with ip-addresses are + * a part of netlink routing interface). + */ + void fill_rtnetlink_request (nlmsghdr &hdr, + int type, + void *data, + int data_length); + + enum + { + COMMAND_TIMEOUT = -3, + COMMAND_RECV_ERROR = -2, + COMMAND_ERROR = -1, + COMMAND_SUCCESS = 0 + }; + + // Mark command status in handle_input (). + void on_recv_error () { this->command_status_ = COMMAND_RECV_ERROR; } + void on_command_success () { this->command_status_ = COMMAND_SUCCESS; } + void on_command_error () { this->command_status_ = COMMAND_ERROR; } + + // The socket. + ACE_SOCK_Netlink socket_ ; + + // The address of the socket. + ACE_Netlink_Addr address_ ; + + // Message sequence number. + ACE_UINT32 seq_ ; + + // The request structure passed to kernel. + Netlink_Request netlink_request_; + + // ip-addr-slash-mask + char ip_buff_[32]; + + // interface + char if_buff_[16]; + + // Buffer to receive messages. Not too large? + char recv_buff_[1024]; + + // The state of the handler. + int state_; + + // The status of the command. + int command_status_; +}; + +Secondary_Ipaddr_Handler::Secondary_Ipaddr_Handler () + : + socket_ (), + address_ (), + seq_ (0), + state_ (START), + command_status_ (COMMAND_SUCCESS) +{ +} + +Secondary_Ipaddr_Handler::~Secondary_Ipaddr_Handler () +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Secondary_Ipaddr_Handler::~Secondary_Ipaddr_Handler \n"))); + this->close (); +} +int +Secondary_Ipaddr_Handler::open (ACE_Reactor *const reactor, + char* const ip_slash_mask, + const char *const if_name) +{ + if (!reactor || + !ip_slash_mask || !ACE_OS::strlen (ip_slash_mask) || + !if_name || !ACE_OS::strlen (if_name)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::open: error ") + ACE_TEXT("zero pointers or zero length strings used as input. \n")), + -1); + + this->reactor (reactor); + + // Another option is to pass a zero pid to the address, to call open () on socket + // performing binding and after bind () to call getsockbyname to fill the address + + this->address_.set (ACE_OS::getpid (), 0); + + if (this->socket ().open (this->address_, + ACE_PROTOCOL_FAMILY_NETLINK, + NETLINK_ROUTE) == -1) + //FUZZ: disable check_for_lack_ACE_OS + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P|%t) Secondary_Ipaddr_Handler::open: - failed \n") + ACE_TEXT("to initialize netlink socket bu open (). \n")), + -1); + //FUZZ: enable check_for_lack_ACE_OS + + // register with the reactor for input + if (this->reactor ()->register_handler (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P|%t) Secondary_Ipaddr_Handler::open - ") + ACE_TEXT("can't register with reactor for handling input.\n")), + -1); + + if (this->reactor ()->schedule_timer (this, + 0, + ACE_Time_Value::zero) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::open - ") + ACE_TEXT("can't schedule timer with reactor.\n")), + -1); + + this->seq_ = ACE_OS::time (0); + + ACE_OS::strncpy (this->ip_buff_, + ip_slash_mask, + sizeof this->ip_buff_); + + ACE_OS::strncpy (this->if_buff_, + if_name, + sizeof this->if_buff_); + + return 0; +} + +ACE_HANDLE +Secondary_Ipaddr_Handler::get_handle (void) const +{ + return this->socket_.get_handle (); +} + +int +Secondary_Ipaddr_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - entered \n"))); + + nlmsghdr *hdr = 0; + iovec iov; + + iov.iov_base = this->recv_buff_; + iov.iov_len = sizeof (this->recv_buff_); + + int rval_bytes = -1; + ACE_Netlink_Addr raddr; + raddr.set (0, 0); + + rval_bytes = this->socket ().recv (&iov, 1, raddr); + switch (rval_bytes) + { + case -1: // Complain and leave + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - ") + ACE_TEXT("%p bad read\n"), ACE_TEXT("client")), + -1); + + case 0: // Complain and leave + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - "), + ACE_TEXT("eof, closing daemon (fd = %d)\n"), + this->get_handle ()), + -1); + + default: + if (raddr.get_size () != sizeof (sockaddr_nl)) + { + this->on_recv_error (); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%n %P) Secondary_Ipaddr_Handler::handle_input - ") + ACE_TEXT("address length not equal sockaddr_nl\n")), + -1); + } + + hdr = reinterpret_cast <nlmsghdr*> (this->recv_buff_); + + if (static_cast <int> (hdr->nlmsg_len) != rval_bytes) + { + this->on_recv_error (); + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - ") + ACE_TEXT("size of nlmsg_len not equal received bytes\n")), + -1); + } + + if (static_cast <int> (hdr->nlmsg_pid) != this->address_.get_pid () || hdr->nlmsg_seq != this->seq_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - ") + ACE_TEXT("process id or message sequence is different \n")), + -1); + + if (hdr->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = static_cast <struct nlmsgerr*> (NLMSG_DATA(hdr)); + + errno = -err->error; + if (errno == 0) + { + this->on_command_success (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - command success \n"))); + return 0; + } + + this->on_command_error (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - command informs about error \n"))); + + // some error message + ACE_OS::perror("rtnetlink error message: "); + + return 0; + } + } + return -1; +} + +int +Secondary_Ipaddr_Handler::handle_timeout (ACE_Time_Value const &, + void const *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - entered \n"))); + + if (this->command_status_ != COMMAND_SUCCESS && + (this->command_status_ != COMMAND_ERROR && + this->state_ == IP_DELETED)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ") + ACE_TEXT("previous command failed\n")), + -1); + } + else + this->command_status_ = COMMAND_TIMEOUT; //invalidate command status + + switch (this->state_) + { + case START: //delete the ip, if it presents + if (this->delete_ip (this->ip_buff_, this->if_buff_) == -1) + { + this->state_ = FAILED; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ") + ACE_TEXT("delete_ip failed\n")), + -1); + } + break; + + case IP_DELETED: // add the ip + if (this->add_ip (this->ip_buff_, this->if_buff_) == -1) + { + this->state_ = FAILED; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ") + ACE_TEXT("add_ip failed\n")), + -1); + } + break; + + case IP_ADDED: //delete added ip to make cleanup + if (dont_cleanup_added_ip) + { + this->state_ = SUCCESS; + return 0; + } + else + { + if (this->delete_ip (this->ip_buff_, this->if_buff_) == -1) + { + this->state_ = FAILED; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ") + ACE_TEXT("delete_ip failed\n")), + -1); + } + } + break; + + case IP_CLEANUPED: + this->state_ = SUCCESS; + return 0; + + default: + return -1; + } + + this->schedule_one_sec_timer (); + this->state_++; + return 0; +} + +int +Secondary_Ipaddr_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P|%t) Secondary_Ipaddr_Handler::handle_close \n"))); + this->close (); + return 0; +} + +int +Secondary_Ipaddr_Handler::close () +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::close \n"))); + + if (this->reactor ()) + { + this->reactor ()->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); + + this->reactor ()->cancel_timer (this); + + this->reactor (0); + } + return 0; +} + +int +Secondary_Ipaddr_Handler::schedule_one_sec_timer () +{ + const ACE_Time_Value one_sec (1, 0); + + if (this->reactor ()->schedule_timer (this, + 0, + one_sec) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::schedule_one_sec_timer - ") + ACE_TEXT("can't schedule timer with reactor.\n")), + -1); + return 0; +} + +ACE_SOCK_Netlink& +Secondary_Ipaddr_Handler::socket () +{ + return this->socket_; +} + +int +Secondary_Ipaddr_Handler::fill_inet_prefix ( + Inet_Prefix &inet_prefix, + const char*const ip_slash_netmask) +{ + ACE_OS::memset (&inet_prefix, 0, sizeof inet_prefix); + char* slash = const_cast <char *> (ACE_OS::strchr (ip_slash_netmask, '/')); + if (slash) + *slash = '\0'; // The idea from Igor Potulnitsky + + inet_prefix.family = AF_INET; // another option is AF_UNSPEC + inet_prefix.bytelen = 4; + inet_prefix.bitlen = -1; + + char ip_buff[32]; + ACE_OS::strncpy (ip_buff, ip_slash_netmask, sizeof (ip_buff)); + + char* to_search = ip_buff, *dot = 0; + + for (int i = 0; i < 4; i++) + { + if (i < 3) + { + dot = ACE_OS::strchr (to_search, '.'); + if (!dot || !ACE_OS::strlen (to_search)) + return -1; + else + *dot = '\0'; + } + int num = ACE_OS::atoi (to_search); + if (num < 0 || num > 255) + return -1; + else + ((u_char *) &inet_prefix.data)[i] = (u_char)num; + + if (i < 3) + to_search = dot + 1; + } + + inet_prefix.bitlen = 32; // AF_INET: 32 + + if (slash) + { + int mask_len = 0; + mask_len = ACE_OS::atoi (slash + 1); + if (mask_len >= 0) + inet_prefix.bitlen = mask_len; + *slash = '/'; + } + return 0; +} + +void +Secondary_Ipaddr_Handler::fill_rtnetlink_request ( + nlmsghdr &hdr, + int type, + void *data, + int data_length) +{ + // points to the end of the aligned header + struct rtattr *rta = reinterpret_cast <struct rtattr*> (((reinterpret_cast <char*>(&hdr)) + NLMSG_ALIGN (hdr.nlmsg_len))); + + rta->rta_type = type; + rta->rta_len = RTA_LENGTH (data_length); + ACE_OS::memcpy (RTA_DATA(rta), data, data_length); + hdr.nlmsg_len = NLMSG_ALIGN (hdr.nlmsg_len) + RTA_LENGTH (data_length); +} + +int +Secondary_Ipaddr_Handler::dispatch_ip_operation ( + char* const ip_slash_mask, + const char *const if_name, + bool action) +{ + if (this->init_netlink_request (ip_slash_mask, + if_name, + this->netlink_request_, + action) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Secondary_Ipaddr_Handler::ip_operation - " + "init_netlink_request () failed.\n"), + -1); + + this->netlink_request_.nhdr_.nlmsg_seq = ++this->seq_; + this->netlink_request_.nhdr_.nlmsg_flags |= NLM_F_ACK; + + iovec iov_send = + { + static_cast <void*> (&this->netlink_request_.nhdr_), + this->netlink_request_.nhdr_.nlmsg_len + }; + + ACE_Netlink_Addr addr_send; + addr_send.set (0, 0); + + if (this->socket ().send (&iov_send, + 1, + addr_send) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("Secondary_Ipaddr_Handler::ip_operation - ") + ACE_TEXT("send of request failed with errno %d.\n"), + errno), + -1); + return 0; +} + +int +Secondary_Ipaddr_Handler::delete_ip ( + char* const ip_slash_mask, + const char *const if_name) +{ + if (this->dispatch_ip_operation ( + ip_slash_mask, + if_name, + false) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::delete_ip - ") + ACE_TEXT("dispatch_ip_operation() failed.\n")), + -1); + return 0; +} + +int +Secondary_Ipaddr_Handler::add_ip ( + char* const ip_slash_mask, + const char *const if_name) +{ + if (this->dispatch_ip_operation (ip_slash_mask, + if_name, + true) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) Secondary_Ipaddr_Handler::add_ip - ") + ACE_TEXT("dispatch_ip_operation() failed.\n")), + -1); + return 0; +} + +int +Secondary_Ipaddr_Handler::init_netlink_request ( + char* const ip_slash_netmask, + const char *const if_name, + Netlink_Request& net_req, + bool action) +{ + ACE_OS::memset (&net_req, 0, sizeof(net_req)); + + // fill the request header + net_req.nhdr_.nlmsg_len = + NLMSG_LENGTH (sizeof(struct ifaddrmsg)); + net_req.nhdr_.nlmsg_flags = NLM_F_REQUEST; + net_req.nhdr_.nlmsg_type = action ? RTM_NEWADDR : RTM_DELADDR; + net_req.ifa_.ifa_family = AF_INET; + + int interface_index = -1; + if (get_if_index (if_name, + interface_index) == -1 || interface_index < 0) + { + ACE_OS::fprintf (stderr, "get_if_index () - failed\n"); + return -1; + } + net_req.ifa_.ifa_index = interface_index; + + Inet_Prefix local_prefix; + + if (fill_inet_prefix (local_prefix, + ip_slash_netmask) == -1) + { + ACE_OS::fprintf (stderr, "fill_inet_prefix () - failed\n"); + return -1; + } + + fill_rtnetlink_request (net_req.nhdr_, + IFA_LOCAL, + &local_prefix.data, + local_prefix.bytelen); + + net_req.ifa_.ifa_prefixlen = local_prefix.bitlen; // number of bits in netmask + net_req.ifa_.ifa_scope = 0; + + return 0; +} + +static int run_test (char*const ip_slash_netmask, + const char*const if_name) +{ + Secondary_Ipaddr_Handler sec_ip_handler; + + if (sec_ip_handler.open (ACE_Reactor::instance (), + ip_slash_netmask, + if_name) + == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P) SOCK_Netlink_Test - run_test () failed ") + ACE_TEXT("due to sec_ip_handler.open () error.\n")), + -1); + + ACE_Time_Value wait_time (4, 0); + + ACE_Reactor::instance()->run_reactor_event_loop (wait_time); + + if (sec_ip_handler.get_state () != Secondary_Ipaddr_Handler::SUCCESS) + return -1; + + return 0; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + if (argc == 1) // one button test + { + one_button_test = 1; + return 0; + } + + ACE_OS::memset (ip_slash_mask, 0, sizeof ip_slash_mask); + ACE_OS::memset (net_dev_name, 0, sizeof net_dev_name); + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:di:")); + int c = 0, ip_len = 0, if_len = 0; + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'a': // ip_slash_netmask_bits + ACE_OS::strcpy (ip_slash_mask, + reinterpret_cast <const char*> (get_opt.opt_arg ())); + + if (! (ip_len = ACE_OS::strlen (ip_slash_mask))) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("%s, -a should be followed by a dotted ipv4 addr slash netmask bits.\n") + ACE_TEXT("Example: \"-a 192.168.1.1/24\" .\n"), + ACE_TEXT("SOCK_Netlink_Test"))); + return -1; + } + break; + + case 'd': + dont_cleanup_added_ip = 1; + break; + + case 'i': // name of the interface + ACE_OS::strcpy (net_dev_name, + reinterpret_cast <const char*> (get_opt.opt_arg ())); + if (! (if_len = ACE_OS::strlen (net_dev_name))) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT("%s, -i should be followed by a valid name of network interface.\n"), + ACE_TEXT("SOCK_Netlink_Test"))); + return -1; + } + break; + + default: + break; + } + } + + if ((ip_len == 0 && if_len) || (ip_len && if_len == 0)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("%s - error: both options -a and -i should be provided. \n"), + ACE_TEXT("SOCK_Netlink_Test")), + -1); + } + else if (ip_len == 0 && if_len == 0) + { + one_button_test = 1; + } + + return 0; +} + +#define DEFAULT_IP_SLASH_MASK "192.168.1.100/24" +#define DEFAULT_NET_DEVICE_NAME "lo" + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Netlink_Test")); + + if (ACE_OS::geteuid ()) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Process has no superuser priveleges. ") + ACE_TEXT ("Unable to run this test.\n"))); + } + else + { + if (::parse_args (argc, argv) == -1) + { + return -1; + } + + if (one_button_test) + { + ACE_OS::strcpy (ip_slash_mask, + DEFAULT_IP_SLASH_MASK); + ACE_OS::strcpy (net_dev_name, + DEFAULT_NET_DEVICE_NAME); + } + + int rval = -1; + if ((rval = run_test (ip_slash_mask, + net_dev_name)) < 0) + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("run_test() failed with rval returned %d. "), + rval)); + return -1; + } + } + + ACE_END_TEST; + + return 0; +} + +#else /* ACE_HAS_NETLINK*/ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Netlink_Test")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT("(%P|%t|%T) \"SOCK_Netlink_Test\" main() - ") + ACE_TEXT("Linux netlink socket support not configured.\n") + ACE_TEXT("#define ACE_HAS_NETLINK = 1 in your config.h ") + ACE_TEXT("file to enable and run the process as a superuser.\n"))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_NETLINK*/ + diff --git a/ACE/tests/SOCK_SEQPACK_SCTP_Test.cpp b/ACE/tests/SOCK_SEQPACK_SCTP_Test.cpp new file mode 100644 index 00000000000..154524191f7 --- /dev/null +++ b/ACE/tests/SOCK_SEQPACK_SCTP_Test.cpp @@ -0,0 +1,398 @@ +// $Id$ +// +// *WARRANTY DISCLAIMER: LIMITATION OF LIABILITY. THE SOFTWARE AND +// CONTENT ARE PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED +// REPRESENTATIONS, GUARANTEES, OR WARRANTIES, INCLUDING BUT NOT LIMITED +// TO SUCH REPRESENTATION, GUARANTEES OR WARRANTIES REGARDING THE +// USABILITY, SUITABILITY, CONDITION, OPERATION OR ACCURACY THEREOF. * +// +// *ALL OTHER WARRANTIES AND CONDITIONS (EXPRESS, IMPLIED OR STATUTORY) +// ARE HEREBY DISCLAIMED, SUCH WARRANTIES AND CONDITIONS INCLUDING +// WITHOUT LIMITATION, ALL WARRANTIES AND CONDITIONS OF MERCHANTABILITY, +// TITLE, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, +// COMPATIBILITY, AND SECURITY OR ACCURACY.* +// +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_SEQPACK_SCTP_Test.cpp +// +// = DESCRIPTION +// Performs several tests on the ACE_SOCK_SEQPACK_Connector, +// ACE_SOCK_SEQPACK_Acceptor, and ACE_SOCK_SEQPACK_Association classes +// specifically for SCTP using the loopback interface. Attempts to +// replicate behavior of SOCK_Test.cpp, but integrating IPv6 tests +// directly. +// +// = AUTHOR +// Dave Craig <dwc@qualcomm.com> +// + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/SOCK_SEQPACK_Connector.h" +#include "ace/SOCK_SEQPACK_Acceptor.h" +#include "ace/Thread_Manager.h" +#include "ace/Handle_Set.h" + +#define TTCPPORT 5001 +#define BYTE_MESG 0xcd + +struct tdesc { + ACE_Thread_Semaphore *tsemap; + bool ipv6_test; +}; + +typedef struct tdesc tdesc_t; + +#ifdef ACE_WIN64 +// This arg is ignored on Windows and causes pointer truncation +// warnings on 64-bit compiled. +#define SELECT_WIDTH(x) 0 +#else +#define SELECT_WIDTH(x) (x) +#endif + +ACE_THR_FUNC_RETURN +Server (void *arg) +{ + ACE_SOCK_SEQPACK_Acceptor *AcceptorSocket = + reinterpret_cast<ACE_SOCK_SEQPACK_Acceptor *> (arg); + + ACE_SOCK_SEQPACK_Association Stream; + + ACE_Handle_Set handle_set; + + const ACE_Time_Value def_timeout (ACE_DEFAULT_TIMEOUT); + + ACE_Time_Value tv (def_timeout); + + int select_width; + + int result; + + // + // Make sure AcceptorSocket is in nonblocking mode so as not to + // hang tests. + // + if (-1 == AcceptorSocket->enable (ACE_NONBLOCK)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("AcceptorSocket.enable (ACE_NONBLOCK)"))); + } + + // + // Set up select to wait for I/O events. + // + handle_set.reset (); + handle_set.set_bit (AcceptorSocket->get_handle ()); + + select_width = SELECT_WIDTH(int (AcceptorSocket->get_handle ()) + 1); + + result = ACE_OS::select(select_width, + handle_set, + 0, + 0, + &tv); + + ACE_ASSERT (tv == def_timeout); + + if (-1 == result) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select")), + 0); + } + else if (0 == result) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT("(%P|%t) select timed out, shutting down\n")), + 0); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) waiting for client to connect\n"))); + + while (-1 != AcceptorSocket->accept (Stream)) { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client connected\n"))); + + // + // Enable non-blocking I/O. + // + if (Stream.enable (ACE_NONBLOCK)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Stream.enable (ACE_NONBLOCK)")), + 0); + } + + unsigned char byte = BYTE_MESG; + + if (-1 == Stream.send_n (&byte, 1)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Stream.send_n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) byte sent\n"))); + + // + // Ubruptly terminate the association. + // + if (-1 == Stream.abort ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Association.abort"))); + } + + // + // Negative test: make sure that we cannot send on a closed association. + // + if (-1 != Stream.send_n (&byte, 1)) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Negative test fail: Association") + ACE_TEXT(".send_n succeeded after abort()\n"))); + //FUZZ: enable check_for_lack_ACE_OS + } + + } + + // + // Close server socket. + // + if (-1 == AcceptorSocket->close ()) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("AcceptorSocket.close"))); + } + + return 0; +} + +ACE_THR_FUNC_RETURN +Client(void *arg) +{ + ACE_Multihomed_INET_Addr *ServerAddr = + reinterpret_cast<ACE_Multihomed_INET_Addr *> (arg); + + ACE_SOCK_SEQPACK_Connector Connector; + + ACE_SOCK_SEQPACK_Association Stream; + + ACE_Time_Value tv (ACE_DEFAULT_TIMEOUT); + + char b; + size_t bytes; + + if (-1 == Connector.connect (Stream, + *ServerAddr, + &tv, + ACE_Addr::sap_any, + 1)) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p to %s:%d\n"), + ACE_TEXT ("Connector.connect"), + ServerAddr->get_host_name (), + ServerAddr->get_port_number ()), + 0); + } + + if (-1 == Stream.disable (ACE_NONBLOCK)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Association.disable (ACE_NONBLOCK)"))); + } + + + if (-1 == Stream.recv_n (&b, 1, &tv, &bytes)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Association.recv_n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Client received %d bytes\n"), + bytes)); + ACE_ASSERT(1 == bytes); + + // + // Give server a little time to abort the association. + // + ACE_OS::sleep(1); + + if (-1 != Stream.recv_n (&b, 1, &tv, &bytes)) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Negative test failed Association") + ACE_TEXT (".recv_n succeeded after abort()\n"))); + //FUZZ: enable check_for_lack_ACE_OS + } + + return 0; +} + +// +// Spawn server and client threads and then wait until they complete the +// test. There must be a timeout on the wait, so executable does not hang the +// tests indefinitely. +// +int +spawn_test(bool ipv6_test) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawn_test started ipv6 %d\n"), + ipv6_test)); + + ACE_SOCK_SEQPACK_Acceptor AcceptorSocket; + + ACE_Multihomed_INET_Addr ServerAddr (TTCPPORT, +#ifdef ACE_HAS_IPV6 + (ipv6_test ? + ACE_IPV6_LOCALHOST : + ACE_LOCALHOST) +#else /* ! ACE_HAS_IPV6 */ + ACE_LOCALHOST +#endif /* ! ACE_HAS_IPV6 */ + ); + + if (-1 == AcceptorSocket.open (ServerAddr, + 1, +#ifdef ACE_HAS_IPV6 + (ipv6_test ? AF_INET6 : AF_INET), +#else /* ! ACE_HAS_IPV6 */ + AF_INET, +#endif /* ! ACE_HAS_IPV6 */ + ACE_DEFAULT_BACKLOG +#if defined (IPPROTO_SCTP) + ,IPPROTO_SCTP +#endif /* IPPROTO_SCTP */ + )) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("AcceptorSocket.open"))); + } + + if (-1 == AcceptorSocket.get_local_addr (ServerAddr)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("AcceptorSocket.get_local_addr"))); + } + + struct sockaddr_in inaddr; + + ServerAddr.get_addresses(&inaddr, 1); + + ACE_ASSERT ((TTCPPORT == ServerAddr.get_port_number ())); + ACE_ASSERT ((ipv6_test || INADDR_LOOPBACK == ServerAddr.get_ip_address ())); + ACE_ASSERT ((!ipv6_test || + ACE_Multihomed_INET_Addr(TTCPPORT, "::1") == ServerAddr)); + +#ifndef ACE_LACKS_FORK + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p%a"), + ACE_TEXT ("fork failed"))); + break; + case 0: + Client (&ServerAddr); + ACE_OS::exit (0); + break; + default: + Server (reinterpret_cast<void *> (&AcceptorSocket)); + ACE_OS::wait (); + break; + } +#elif defined (ACE_HAS_THREADS) + if (-1 == ACE_Thread_Manager::instance ()->spawn + (Server, + reinterpret_cast<void *> (&AcceptorSocket), + THR_NEW_LWP | THR_DETACHED)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p%a"), + ACE_TEXT ("thread create failed"))); + } + + if (-1 == ACE_Thread_Manager::instance ()->spawn + (Client, + reinterpret_cast<void *> (&ServerAddr), + THR_NEW_LWP | THR_DETACHED)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p%a"), + ACE_TEXT ("thread create failed"))); + } + + ACE_Thread_Manager::instance ()->wait (); +#else /* ACE_LACKS_FORK && ! ACE_HAS_THREADS */ + ACE_ERROR ((LM_DEBUG, + ACE_TEXT ("(%P|%t) \n"), + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_LACKS_FORK && ! ACE_HAS_THREADS */ + + return 0; +} + +int +do_test(void) +{ + spawn_test(false); + +#ifdef ACE_HAS_IPV6 + spawn_test(true); +#endif + + return 0; +} + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_START_TEST (ACE_TEXT ("SOCK_SEQPACK_SCTP_Test")); + + // + // Check whether host OS has SCTP support before starting this test. + // If not, just pass because there is not a hope of testing + // SOCK_SEQPACK. + // + int status = 0; + +#ifdef ACE_HAS_SCTP + status = do_test(); +#else /* ! ACE_HAS_SCTP */ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("SCTP not supported by ACE.\n") + ACE_TEXT("This test will not do anything.\n"))); +#endif /* ! ACE_HAS_SCTP */ + + ACE_END_TEST; + + return status; +} + diff --git a/ACE/tests/SOCK_Send_Recv_Test.cpp b/ACE/tests/SOCK_Send_Recv_Test.cpp new file mode 100644 index 00000000000..a476e3071cf --- /dev/null +++ b/ACE/tests/SOCK_Send_Recv_Test.cpp @@ -0,0 +1,455 @@ +// $Id$ + +// =========================================================================== +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_Send_Recv_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_SOCK>'s various send and receive +// methods. The test forks two processes or spawns two threads +// (depending upon the platform) and then executes client and +// server allowing them to connect and exchange data in ways +// designed to exercise the send and recv functions. +// +// Right now, it primarily tests the iov-like send and recv +// functions, but others should be added to completely cover the +// possible scenarios. +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Time_Value.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" + +ACE_RCSID(tests, SOCK_Send_Recv_Test, "$Id$") + +// Change to non-zero if test fails +static int Test_Result = 0; + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +// In test 3, a large amount of data is sent. The purpose is to overflow the +// TCP send window, causing the sender to block (it's a send_n). This value +// is the amount to send. The assumption is that no implementation has a +// receive window larger than 128K bytes. If one is found, this is the place +// to change it. +// For some odd reason, NT will try to send a single large buffer, but not +// multiple smaller ones that add up to the large size. +const size_t Test3_Send_Size = 4*1024; +const size_t Test3_Loops = 10; +const size_t Test3_Total_Size = Test3_Send_Size * Test3_Loops; + + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = reinterpret_cast<ACE_INET_Addr *> (arg); + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_LOCALHOST); + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Connecting to port %d\n"), + server_addr.get_port_number())); + + // Initiate connection with server; don't wait forever + if (con.connect (cli_stream, + server_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + Test_Result = 1; + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) connected to %s\n"), + ACE_TEXT_CHAR_TO_TCHAR(server_addr.get_host_name ()))); + + //******************* TEST 0 ****************************** + // + // First make sure that non-blocking receive works as intended. + // Set the socket to non-blocking and do a recv() - the server side + // will send a short piece of data after sleeping for a few seconds. + // This means we should get a EWOULDBLOCK first; then change to + // blocking for the rest of the tests. + + u_char buffer[255]; + size_t i; + ssize_t len; + + if (-1 == cli_stream.enable (ACE_NONBLOCK)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Can't enable test 0 nonblocking"))); + + len = cli_stream.recv (buffer, sizeof (buffer)); + cli_stream.disable (ACE_NONBLOCK); + if (len == -1) + { + if (errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Nonblocking recv ok!\n"))); + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Nonblocking recv"))); + Test_Result = 1; + } + cli_stream.recv (buffer, sizeof (buffer)); // Drain the sent data + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) Nonblocking recv blocked\n"))); + Test_Result = 1; + } + + //******************* TEST 1 ****************************** + // + // Do a iovec sendv - send the 255 byte buffer in 5 chunks. The + // server will verify that the correct data is sent, and that there + // is no more and no less. + + // The server will verify that this data pattern gets there intact. + + for (i = 0; i < sizeof buffer; ++i) + buffer[i] = static_cast<u_char> (i); + + iovec iov[5]; + + iov[0].iov_base = reinterpret_cast<char *> (&buffer[0]); + iov[0].iov_len = 50; + + iov[1].iov_base = reinterpret_cast<char *> (&buffer[50]); + iov[1].iov_len = 25; + + iov[2].iov_base = reinterpret_cast<char *> (&buffer[75]); + iov[2].iov_len = 150; + + iov[3].iov_base = reinterpret_cast<char *> (&buffer[225]); + iov[3].iov_len = 29; + + iov[4].iov_base = reinterpret_cast<char *> (&buffer[254]); + iov[4].iov_len = 1; + + len = cli_stream.sendv (iov, 5); + if (len == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Test 1, sendv failed"))); + Test_Result = 1; + } + else if (len != 255) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) test 1 recvd %d; should be 255\n"), + static_cast<int> (len))); + + //******************* TEST 2 ****************************** + // + // The same data is coming back - receive it using recv (size_t n, + // ...) and compare it to the original data. + + u_char buffer2[255]; + // Give it a chance to get here + ACE_OS::sleep (2); + len = cli_stream.recv (4, + buffer2, + 150, + &buffer2[150], + 105); + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p; len is %d, but should be 255!\n"), + len)); + } + + for (i = 0; i < 255; i++) + if (buffer2[i] != buffer[i]) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 2, rcvd byte %d is %d, not %d\n"), + i, buffer2[i], buffer[i])); + Test_Result = 1; + } + + //******************* TEST 3 ****************************** + // + // Do a send_n of a large size. The receive should sleep some to + // cause the data reception to be delayed, which will fill up the + // TCP window and cause send_n to block at some point. The particular + // case this tests only needs to be exercised if the socket is + // non-blocking, so set that first. + + ssize_t sent; + char buff[Test3_Send_Size]; + if (-1 == cli_stream.enable (ACE_NONBLOCK)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Can't enable test 3 nonblocking"))); + for (i = 0; i < Test3_Loops; ++i) + { + errno = 0; + sent = cli_stream.send_n (buff, sizeof (buff)); + if (sent != sizeof (buff) && errno != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 3, pass %d, sent %d, %p\n"), + i, sent, ACE_TEXT ("error"))); + Test_Result = 1; // Fail + } + } + + cli_stream.close (); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Acceptor *peer_acceptor = (ACE_SOCK_Acceptor *) arg; + ACE_SOCK_Stream sock_str; + ACE_INET_Addr cli_addr; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + // Accept the connection over which the stream tests will run. + // Don't lock up if client doesn't connect + if (peer_acceptor->accept (sock_str, + &cli_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("accept"))); + Test_Result = 1; + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client %s connected from %d\n"), + ACE_TEXT_CHAR_TO_TCHAR(cli_addr.get_host_name ()), + cli_addr.get_port_number ())); + + //******************* TEST 1 ****************************** + // + // The client will be expecting to not see data for a few seconds. + // Wait a bit then send a short blurb to complete the client's recv. + + ACE_OS::sleep (3); + sock_str.send ("abc", 3); + + //******************* TEST 1 ****************************** + // + // Do a iovec recvv - the client should send 255 bytes, which we + // will be detected and read into a ACE-allocated buffer. Use a 5 + // second timeout to give the client a chance to send it all. + + ACE_OS::sleep (5); + + iovec iov[3]; + u_char buffer[255]; + ssize_t len; + int i; + + iov[0].iov_base = reinterpret_cast<char *> (&buffer[0]); + iov[0].iov_len = 75; + + iov[1].iov_base = reinterpret_cast<char *> (&buffer[75]); + iov[1].iov_len = 100; + + iov[2].iov_base = reinterpret_cast<char *> (&buffer[175]); + iov[2].iov_len = 80; + + len = sock_str.recvv_n (iov, 3); + if (len == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Test 1, recvv failed"))); + Test_Result = 1; + } + + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 1 recvd %d; should be 255\n"), + static_cast<int> (len))); + Test_Result = 1; + } + for (i = 0; i < 255; i++) + if (buffer[i] != i) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 1, rcvd byte %d is %d, not %d\n"), + i, + buffer[i], + i)); + Test_Result = 1; + } + + //******************* TEST 2 ****************************** + // + // Send the buffer back, using send (size_t n, ...) in 3 pieces. + + len = sock_str.send (6, + buffer, + 42, + &buffer[42], + 189, + &buffer[231], + 24); + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 2 sent %d; should be 255\n"), + static_cast<int> (len))); + Test_Result = 1; + } + + //******************* TEST 3 ****************************** + // + // The sender is testing send_n to make sure it blocks if the TCP + // window fills. So sleep here for a bit to avoid getting the data + // yet. Then just read and empty out the received data. + ACE_OS::sleep (8); + // Keep reading until the peer closes. + sock_str.disable (ACE_NONBLOCK); + ssize_t got = 1; + size_t total_recv = 0; + while (got != 0) + { + errno = 0; + got = sock_str.recv (buffer, sizeof (buffer)); + if (got < 0) + break; + total_recv += got; + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Test 3 received %d bytes\n"), + total_recv)); + + if (total_recv == Test3_Total_Size) + { + if (got != 0 || errno != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 3 final recv status %d, expected 0\n"), + got)); + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("expected errno == 0, instead"))); + Test_Result = 1; // Fail + } + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) Test 3 expected %d %p\n"), + Test3_Total_Size, ACE_TEXT ("bytes"))); + Test_Result = 1; + } + + sock_str.close(); + + return 0; +} + +#endif /* !ACE_LACKS_FORK || ACE_HAS_THREADS */ + +static void +spawn (void) +{ + // Acceptor + ACE_SOCK_Acceptor peer_acceptor; + + // Create a server address. + ACE_INET_Addr server_addr; + + // Bind listener to any port and then find out what the port was. + if (peer_acceptor.open (ACE_Addr::sap_any) == -1 + || peer_acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("open"))); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + /* NOTREACHED */ + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server (reinterpret_cast<void *> (&peer_acceptor)); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + reinterpret_cast<void *> (&peer_acceptor), + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + reinterpret_cast<void *> (&server_addr), + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + peer_acceptor.close (); + } +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Send_Recv_Test")); + + spawn (); + + ACE_END_TEST; + return Test_Result; +} diff --git a/ACE/tests/SOCK_Send_Recv_Test_IPV6.cpp b/ACE/tests/SOCK_Send_Recv_Test_IPV6.cpp new file mode 100644 index 00000000000..b630ffb00ec --- /dev/null +++ b/ACE/tests/SOCK_Send_Recv_Test_IPV6.cpp @@ -0,0 +1,399 @@ +// $Id$ +// =========================================================================== +/** + * @file SOCK_Send_Recv_Test_IPV6.cpp + * + * @brief This is a test of the <ACE_SOCK>'s various send and receive + * methods. + + * The test forks two processes or spawns two threads (depending upon + * the platform) and then executes client and server allowing them to + * connect and exchange data in ways designed to exercise the send + * and recv functions. Right now, it primarily tests the iov-like + * send and recv functions, but others should be added to completely + * cover the possible scenarios. + * + * @author Steve Huston <shuston@riverace.com> + * Brian Buesker <bbuesker@qualcomm.com> + */ +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Time_Value.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" + +// Change to non-zero if test fails +static int Test_Result = 0; + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +// In test 3, a large amount of data is sent. The purpose is to overflow the +// TCP send window, causing the sender to block (it's a send_n). This value +// is the amount to send. The assumption is that no implementation has a +// receive window larger than 128K bytes. If one is found, this is the place +// to change it. +// For some odd reason, NT will try to send a single large buffer, but not +// multiple smaller ones that add up to the large size. +const size_t Test3_Send_Size = 4*1024; +const size_t Test3_Loops = 10; +const size_t Test3_Total_Size = Test3_Send_Size * Test3_Loops; + + +#if defined (ACE_HAS_IPV6) + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = reinterpret_cast<ACE_INET_Addr *> (arg); + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_IPV6_LOCALHOST); + + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Connecting to port %d\n"), + server_addr.get_port_number())); + + // Initiate connection with server; don't wait forever + if (con.connect (cli_stream, + server_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + Test_Result = 1; + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) connected to %s\n"), + ACE_TEXT_CHAR_TO_TCHAR(server_addr.get_host_name ()))); + + //******************* TEST 1 ****************************** + // + // Do a iovec sendv - send the 255 byte buffer in 5 chunks. The + // server will verify that the correct data is sent, and that there + // is no more and no less. + + u_char buffer[255]; + size_t i; + ssize_t len; + + // The server will verify that this data pattern gets there intact. + + for (i = 0; i < sizeof buffer; ++i) + buffer[i] = static_cast<u_char> (i); + + iovec iov[5]; + + iov[0].iov_base = reinterpret_cast<char *> (&buffer[0]); + iov[0].iov_len = 50; + + iov[1].iov_base = reinterpret_cast<char *> (&buffer[50]); + iov[1].iov_len = 25; + + iov[2].iov_base = reinterpret_cast<char *> (&buffer[75]); + iov[2].iov_len = 150; + + iov[3].iov_base = reinterpret_cast<char *> (&buffer[225]); + iov[3].iov_len = 29; + + iov[4].iov_base = reinterpret_cast<char *> (&buffer[254]); + iov[4].iov_len = 1; + + len = cli_stream.sendv (iov, 5); + if (len == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Test 1, sendv failed"))); + Test_Result = 1; + } + else + ACE_ASSERT (len == 255); + + //******************* TEST 2 ****************************** + // + // The same data is coming back - receive it using recv (size_t n, + // ...) and compare it to the original data. + + u_char buffer2[255]; + // Give it a chance to get here + ACE_OS::sleep (2); + len = cli_stream.recv (4, + buffer2, + 150, + &buffer2[150], + 105); + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p; len is %d, but should be 255!\n"), + len)); + } + ACE_ASSERT (len == 255); + + for (i = 0; i < 255; i++) + if (buffer2[i] != buffer[i]) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 2, rcvd byte %d is %d, not %d\n"), + i, buffer2[i], buffer[i])); + Test_Result = 1; + } + + //******************* TEST 3 ****************************** + // + // Do a send_n of a large size. The receive should sleep some to + // cause the data reception to be delayed, which will fill up the + // TCP window and cause send_n to block at some point. The particular + // case this tests only needs to be exercised if the socket is + // non-blocking, so set that first. + + ssize_t sent; + char buff[Test3_Send_Size]; + ACE_ASSERT (cli_stream.enable (ACE_NONBLOCK) != -1); + for (i = 0; i < Test3_Loops; ++i) + { + errno = 0; + sent = cli_stream.send_n (buff, sizeof (buff)); + if (sent != sizeof (buff) && errno != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 3, pass %d, sent %d, %p\n"), + i, sent, ACE_TEXT ("error"))); + Test_Result = 1; // Fail + } + } + + cli_stream.close (); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Acceptor *peer_acceptor = (ACE_SOCK_Acceptor *) arg; + ACE_SOCK_Stream sock_str; + ACE_INET_Addr cli_addr; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + // Accept the connection over which the stream tests will run. + // Don't lock up if client doesn't connect + if (peer_acceptor->accept (sock_str, + &cli_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("accept"))); + Test_Result = 1; + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client %s connected from %d\n"), + ACE_TEXT_CHAR_TO_TCHAR(cli_addr.get_host_name ()), + cli_addr.get_port_number ())); + + //******************* TEST 1 ****************************** + // + // Do a iovec recvv - the client should send 255 bytes, which we + // will be detected and read into a ACE-allocated buffer. Use a 5 + // second timeout to give the client a chance to send it all. + + ACE_OS::sleep (5); + + iovec iov[3]; + u_char buffer[255]; + ssize_t len; + int i; + + iov[0].iov_base = reinterpret_cast<char *> (&buffer[0]); + iov[0].iov_len = 75; + + iov[1].iov_base = reinterpret_cast<char *> (&buffer[75]); + iov[1].iov_len = 100; + + iov[2].iov_base = reinterpret_cast<char *> (&buffer[175]); + iov[2].iov_len = 80; + + len = sock_str.recvv_n (iov, 3); + if (len == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Test 1, recvv failed"))); + Test_Result = 1; + } + + ACE_ASSERT (len == 255); + for (i = 0; i < 255; i++) + if (buffer[i] != i) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 1, rcvd byte %d is %d, not %d\n"), + i, + buffer[i], + i)); + Test_Result = 1; + } + + //******************* TEST 2 ****************************** + // + // Send the buffer back, using send (size_t n, ...) in 3 pieces. + + len = sock_str.send (6, + buffer, + 42, + &buffer[42], + 189, + &buffer[231], + 24); + ACE_ASSERT (len == 255); + + //******************* TEST 3 ****************************** + // + // The sender is testing send_n to make sure it blocks if the TCP + // window fills. So sleep here for a bit to avoid getting the data + // yet. Then just read and empty out the received data. + ACE_OS::sleep (8); + // Keep reading until the peer closes. + sock_str.disable (ACE_NONBLOCK); + ssize_t got = 1; + size_t total_recv = 0; + while (got != 0) + { + errno = 0; + got = sock_str.recv (buffer, sizeof (buffer)); + if (got < 0) + break; + total_recv += got; + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Test 3 received %d bytes\n"), + total_recv)); + + if (total_recv == Test3_Total_Size) + { + if (got != 0 || errno != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 3 final recv status %d, expected 0\n"), + got)); + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("expected errno == 0, instead"))); + Test_Result = 1; // Fail + } + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) Test 3 expected %d %p\n"), + Test3_Total_Size, ACE_TEXT ("bytes"))); + Test_Result = 1; + } + + sock_str.close(); + + return 0; +} + +#endif /*ACE_HAS_IPV6*/ + +#endif /* !ACE_LACKS_FORK || ACE_HAS_THREADS */ + +#if defined (ACE_HAS_IPV6) +static void +spawn (void) +{ + // Acceptor + ACE_SOCK_Acceptor peer_acceptor; + + // Create a server address. + ACE_INET_Addr server_addr; + + // Bind listener to any port and then find out what the port was. + if (peer_acceptor.open (ACE_Addr::sap_any, 0, AF_INET6) == -1 + || peer_acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("open"))); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + /* NOTREACHED */ + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server (reinterpret_cast<void *> (&peer_acceptor)); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + reinterpret_cast<void *> (&peer_acceptor), + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + reinterpret_cast<void *> (&server_addr), + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + peer_acceptor.close (); + } +} + +#endif /*ACE_HAS_IPV6*/ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Send_Recv_Test_IPV6")); + +#if defined (ACE_HAS_IPV6) + spawn (); +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + return Test_Result; +} diff --git a/ACE/tests/SOCK_Test.cpp b/ACE/tests/SOCK_Test.cpp new file mode 100644 index 00000000000..e608a67a873 --- /dev/null +++ b/ACE/tests/SOCK_Test.cpp @@ -0,0 +1,286 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_Test.cpp +// +// = DESCRIPTION +// This is a test of the <ACE_SOCK_Acceptor> and +// <ACE_SOCK_Connector> classes. The test forks two processes or +// spawns two threads (depending upon the platform) and then +// executes client and server allowing them to connect and +// exchange data. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Doug Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/Thread.h" +#include "ace/Time_Value.h" +#include "ace/Thread_Manager.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Handle_Set.h" + +ACE_RCSID(tests, SOCK_Test, "$Id$") + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg; + ACE_INET_Addr server_addr (remote_addr->get_port_number (), ACE_LOCALHOST); + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting non-blocking connect\n"))); + // Initiate timed, non-blocking connection with server. + + // Attempt a non-blocking connect to the server. + if (con.connect (cli_stream, server_addr, + (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + { + if (errno != EWOULDBLOCK) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("connection failed"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting timed connect\n"))); + + // Check if non-blocking connection is in progress, + // and wait up to ACE_DEFAULT_TIMEOUT seconds for it to complete. + ACE_Time_Value tv (ACE_DEFAULT_TIMEOUT); + + if (con.complete (cli_stream, &server_addr, &tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("connection failed")), 0); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) connected to %s\n"), + ACE_TEXT_CHAR_TO_TCHAR(server_addr.get_host_name ()))); + } + + if (cli_stream.disable (ACE_NONBLOCK) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("disable"))); + + // Send data to server (correctly handles "incomplete writes"). + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + if (cli_stream.send_n (c, 1) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("send_n"))); + + // Explicitly close the writer-side of the connection. + if (cli_stream.close_writer () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close_writer"))); + + char buf[1]; + + // Wait for handshake with server. + if (cli_stream.recv_n (buf, 1) != 1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("recv_n"))); + + // Close the connection completely. + if (cli_stream.close () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close"))); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Acceptor *peer_acceptor = (ACE_SOCK_Acceptor *) arg; + + if (peer_acceptor->enable (ACE_NONBLOCK) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("enable"))); + + // Keep these objects out here to prevent excessive constructor + // calls... + ACE_SOCK_Stream new_stream; + ACE_INET_Addr cli_addr; + ACE_Handle_Set handle_set; + const ACE_Time_Value def_timeout (ACE_DEFAULT_TIMEOUT); + ACE_Time_Value tv (def_timeout); + + char buf[BUFSIZ]; + const char *t = ACE_ALPHABET; + + handle_set.reset (); + handle_set.set_bit (peer_acceptor->get_handle ()); + + int select_width; +# if defined (ACE_WIN64) + // This arg is ignored on Windows and causes pointer truncation + // warnings on 64-bit compiles. + select_width = 0; +# else + select_width = int (peer_acceptor->get_handle ()) + 1; +# endif /* ACE_WIN64 */ + int result = ACE_OS::select (select_width, + handle_set, + 0, 0, &tv); + ACE_ASSERT (tv == def_timeout); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("select")), 0); + else if (result == 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) select timed out, shutting down\n"))); + return 0; + } + + // Create a new ACE_SOCK_Stream endpoint (note automatic restart + // if errno == EINTR). + + while ((result = peer_acceptor->accept (new_stream, &cli_addr)) != -1) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) client %s connected from %d\n"), + ACE_TEXT_CHAR_TO_TCHAR(cli_addr.get_host_name ()), cli_addr.get_port_number ())); + + // Enable non-blocking I/O. + if (new_stream.enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("enable")), 0); + + handle_set.reset (); + handle_set.set_bit (new_stream.get_handle ()); + + // Read data from client (terminate on error). + int select_width; + for (ssize_t r_bytes; ;) + { +# if defined (ACE_WIN64) + // This arg is ignored on Windows and causes pointer truncation + // warnings on 64-bit compiles. + select_width = 0; +# else + select_width = int (new_stream.get_handle ()) + 1; +# endif /* ACE_WIN64 */ + if (ACE_OS::select (select_width, + handle_set, + 0, 0, 0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("select")), 0); + + while ((r_bytes = new_stream.recv (buf, 1)) > 0) + { + ACE_ASSERT (*t == buf[0]); + t++; + } + + if (r_bytes == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reached end of input, connection closed by client\n"))); + + // Handshake back with client. + if (new_stream.send_n ("", 1) != 1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("send_n"))); + + // Close endpoint. + if (new_stream.close () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close"))); + return 0; + } + else if (r_bytes == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) no input available, going back to reading\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("recv_n")), 0); + } + } + } + + if (result == -1) + { + if (errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) no connections available, shutting down\n"))); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("accept"))); + } + + return 0; +} + +static void +spawn (void) +{ + // Acceptor + ACE_SOCK_Acceptor peer_acceptor; + + // Create a server address. + ACE_INET_Addr server_addr; + + // Bind listener to any port and then find out what the port was. + if (peer_acceptor.open (ACE_Addr::sap_any) == -1 + || peer_acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("open"))); + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + /* NOTREACHED */ + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server ((void *) &peer_acceptor); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + (void *) &peer_acceptor, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + (void *) &server_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + peer_acceptor.close (); + } +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Test")); + + spawn (); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/SOCK_Test_IPv6.cpp b/ACE/tests/SOCK_Test_IPv6.cpp new file mode 100644 index 00000000000..bea8a7a5587 --- /dev/null +++ b/ACE/tests/SOCK_Test_IPv6.cpp @@ -0,0 +1,290 @@ +// $Id$ +// ============================================================================ +/** + * @file SOCK_Test_IPv6.cpp + * + * @brief This is a test of the <ACE_SOCK_Acceptor> and + * <ACE_SOCK_Connector> classes. + * + * The test forks two processes or spawns two threads (depending upon + * the platform) and then executes client and server allowing them to + * connect and exchange data. + * + * @author Prashant Jain <pjain@cs.wustl.edu> + * Doug Schmidt <schmidt@cs.wustl.edu> + * Brian Buesker <bbuesker@qualcomm.com> + */ +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Time_Value.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Handle_Set.h" + +#if !defined (ACE_LACKS_FORK) +# include "ace/OS_NS_unistd.h" +#endif + +#if defined (ACE_HAS_IPV6) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg; + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_IPV6_LOCALHOST); + + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting non-blocking connect\n"))); + // Initiate timed, non-blocking connection with server. + + // Attempt a non-blocking connect to the server. + if (con.connect (cli_stream, server_addr, + (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + { + if (errno != EWOULDBLOCK) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("connection failed"))); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting timed connect\n"))); + + // Check if non-blocking connection is in progress, + // and wait up to ACE_DEFAULT_TIMEOUT seconds for it to complete. + ACE_Time_Value tv (ACE_DEFAULT_TIMEOUT); + + if (con.complete (cli_stream, &server_addr, &tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("connection failed")), 0); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) connected to %s\n"), + ACE_TEXT_CHAR_TO_TCHAR(server_addr.get_host_name ()))); + } + + if (cli_stream.disable (ACE_NONBLOCK) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("disable"))); + + // Send data to server (correctly handles "incomplete writes"). + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + if (cli_stream.send_n (c, 1) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("send_n"))); + + // Explicitly close the writer-side of the connection. + if (cli_stream.close_writer () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close_writer"))); + + char buf[1]; + + // Wait for handshake with server. + if (cli_stream.recv_n (buf, 1) != 1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("recv_n"))); + + // Close the connection completely. + if (cli_stream.close () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close"))); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Acceptor *peer_acceptor = (ACE_SOCK_Acceptor *) arg; + + if (peer_acceptor->enable (ACE_NONBLOCK) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("enable"))); + + // Keep these objects out here to prevent excessive constructor + // calls... + ACE_SOCK_Stream new_stream; + ACE_INET_Addr cli_addr; + ACE_Handle_Set handle_set; + const ACE_Time_Value def_timeout (ACE_DEFAULT_TIMEOUT); + ACE_Time_Value tv (def_timeout); + + char buf[BUFSIZ]; + const char *t = ACE_ALPHABET; + + handle_set.reset (); + handle_set.set_bit (peer_acceptor->get_handle ()); + + int select_width; +# if defined (ACE_WIN64) + // This arg is ignored on Windows and causes pointer truncation + // warnings on 64-bit compiles. + select_width = 0; +# else + select_width = int (peer_acceptor->get_handle ()) + 1; +# endif /* ACE_WIN64 */ + int result = ACE_OS::select (select_width, + handle_set, + 0, 0, &tv); + ACE_ASSERT (tv == def_timeout); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("select")), 0); + else if (result == 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) select timed out, shutting down\n"))); + return 0; + } + + // Create a new ACE_SOCK_Stream endpoint (note automatic restart + // if errno == EINTR). + + while ((result = peer_acceptor->accept (new_stream, &cli_addr)) != -1) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) client %s connected from %d\n"), + ACE_TEXT_CHAR_TO_TCHAR(cli_addr.get_host_name ()), cli_addr.get_port_number ())); + + // Enable non-blocking I/O. + if (new_stream.enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("enable")), 0); + + handle_set.reset (); + handle_set.set_bit (new_stream.get_handle ()); + + // Read data from client (terminate on error). + int select_width; + for (ssize_t r_bytes; ;) + { +# if defined (ACE_WIN64) + // This arg is ignored on Windows and causes pointer truncation + // warnings on 64-bit compiles. + select_width = 0; +# else + select_width = int (new_stream.get_handle ()) + 1; +# endif /* ACE_WIN64 */ + if (ACE_OS::select (select_width, + handle_set, + 0, 0, 0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("select")), 0); + + while ((r_bytes = new_stream.recv (buf, 1)) > 0) + { + ACE_ASSERT (*t == buf[0]); + t++; + } + + if (r_bytes == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reached end of input, connection closed by client\n"))); + + // Handshake back with client. + if (new_stream.send_n ("", 1) != 1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("send_n"))); + + // Close endpoint. + if (new_stream.close () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("close"))); + return 0; + } + else if (r_bytes == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) no input available, going back to reading\n"))); + else + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("recv_n")), 0); + } + } + } + + if (result == -1) + { + if (errno == EWOULDBLOCK) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) no connections available, shutting down\n"))); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("accept"))); + } + + return 0; +} + +static void +spawn (void) +{ + // Acceptor + ACE_SOCK_Acceptor peer_acceptor; + + // Create a server address. + ACE_INET_Addr server_addr; + + // Bind listener to any port and then find out what the port was. + if (peer_acceptor.open (ACE_Addr::sap_any, 0, AF_INET6) == -1 + || peer_acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("open"))); + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + /* NOTREACHED */ + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server ((void *) &peer_acceptor); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + (void *) &peer_acceptor, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + (void *) &server_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + peer_acceptor.close (); + } +} +#endif /*ACE_HAS_IPV6*/ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SOCK_Test_IPv6")); + +#if defined (ACE_HAS_IPV6) + spawn (); +#endif /* ACE_HAS_IPV6 */ + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/SPIPE_Test.cpp b/ACE/tests/SPIPE_Test.cpp new file mode 100644 index 00000000000..c3b1ae2dd8f --- /dev/null +++ b/ACE/tests/SPIPE_Test.cpp @@ -0,0 +1,220 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// SPIPE_Test.cpp +// +// = DESCRIPTION +// This is a simple test of Named Pipes that uses +// ACE_SPIPE_Acceptor and ACE_SPIPE_Connector classes. The test +// forks two processes or spawns two threads (depending upon the +// platform) and then executes the client and server allowing +// them to use the named pipe to exchange data. No user input is +// required as far as command line arguments are concerned. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/SPIPE_Addr.h" +#include "ace/SPIPE_Connector.h" +#include "ace/SPIPE_Acceptor.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, SPIPE_Test, "SPIPE_Test.cpp,v 4.36 2002/03/06 21:48:03 nanbor Exp") + +#if defined (ACE_HAS_STREAM_PIPES) || defined (ACE_HAS_WIN32_NAMED_PIPES) +# define TEST_HAS_STREAM_PIPES +#endif + +#if defined (TEST_HAS_STREAM_PIPES) + +static const char ACE_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz"; + +// pipe name to use +static const ACE_TCHAR *PIPE_NAME = ACE_TEXT ("ace_pipe_name"); + +static void * +client (void *) +{ + const ACE_TCHAR *rendezvous = PIPE_NAME; + ACE_SPIPE_Stream cli_stream; + ACE_SPIPE_Connector con; + + ACE_OS::sleep (5); + + if (con.connect (cli_stream, ACE_SPIPE_Addr (rendezvous)) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), rendezvous, 1)); + + for (const char *c = ACE_ALPHABET; *c != '\0'; c++) + if (cli_stream.send (c, 1) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("send"))); + + if (cli_stream.close () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("close"))); + +#if defined (ACE_HAS_WIN32_NAMED_PIPES) + + // Wait for server to get ready... + ACE_OS::sleep (1); + + // Connect in bytestream-oriented mode. + if (con.connect (cli_stream, + ACE_SPIPE_Addr (rendezvous), + 0, + ACE_Addr::sap_any, + 0, + O_RDWR, + 0, + 0, + PIPE_READMODE_BYTE) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), rendezvous, 1)); + + + // Write out the alphabet all at once. + if (cli_stream.send_n (ACE_ALPHABET, + ACE_OS::strlen (ACE_ALPHABET)) != (ssize_t) ACE_OS::strlen (ACE_ALPHABET)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("send_n"))); + + // Write out the alphabet one byte at a time + for (const char *d = ACE_ALPHABET; *d != '\0'; d++) + if (cli_stream.send (d, 1) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("send"))); + + if (cli_stream.close () == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("close"))); +#endif + +#if !defined (ACE_WIN32) + ACE_OS::exit (0); +#endif + return 0; +} + +static void * +server (void *) +{ + ACE_SPIPE_Acceptor acceptor; + ACE_SPIPE_Stream new_stream; + char buf[BUFSIZ]; + const char *t = ACE_ALPHABET; + + const ACE_TCHAR *rendezvous = PIPE_NAME; + + // Initialize named pipe listener. + + if (acceptor.open (ACE_SPIPE_Addr (rendezvous)) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("open"), 1)); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("waiting for connection\n"))); + + // Accept a client connection + if (acceptor.accept (new_stream, 0) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("accept"), 1)); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Accepted connection\n"))); + + while (new_stream.recv (buf, 1) > 0) + { + ACE_ASSERT (*t == buf[0]); + t++; + } + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("End of connection. Closing handle\n"))); + new_stream.close (); + acceptor.close (); + +#if defined (ACE_HAS_WIN32_NAMED_PIPES) + // Initialize an NT bytestream named pipe listener. + if (acceptor.open (ACE_SPIPE_Addr (rendezvous), + 1, + 0, + 0, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("open"), 1)); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("waiting for connection\n"))); + + // Accept a client connection + if (acceptor.accept (new_stream, 0) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("accept"), 1)); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Accepted connection\n"))); + + // The client will write the entire buffer at once, verify that we + // can stream it in one byte at a time. + for (t = ACE_ALPHABET; *t; t++) + { + if (new_stream.recv (buf, 1) <= 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("recv"), 1)); + break; + } + ACE_ASSERT (*t == buf[0]); + } + + // Wait for the client to stream in the buffer one byte at a time. + + ACE_OS::sleep (1); + + // Verify that we can read the stream of individual bytes all at + // once. + if (new_stream.recv (buf, sizeof(buf)) != (ssize_t) ACE_OS::strlen (ACE_ALPHABET)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("recv"), 1)); + else + ACE_ASSERT(ACE_OS::memcmp(ACE_ALPHABET, buf, ACE_OS::strlen (ACE_ALPHABET)) == 0); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("End of connection. Closing handle\n"))); + new_stream.close (); + acceptor.close (); +#endif /* defined (ACE_HAS_WIN32NAMED_PIPES) */ + + return 0; +} +#endif /* TEST_HAS_STREAM_PIPES */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SPIPE_Test")); + +#if defined (TEST_HAS_STREAM_PIPES) +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork ()) + { + case -1: + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("fork failed"))); + ACE_OS::exit (-1); + case 0: + client (0); + default: + server (0); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (client), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("thread create failed"))); + + if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (server), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n%a"), ACE_TEXT ("thread create failed"))); + + ACE_Thread_Manager::instance ()->wait (); +#endif /* !ACE_LACKS_EXEC */ +#else + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("SPIPE is not supported on this platform\n"))); +#endif /* TEST_HAS_STREAM_PIPES */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/SSL/Bug_2912_Regression_Test.cpp b/ACE/tests/SSL/Bug_2912_Regression_Test.cpp new file mode 100644 index 00000000000..bdc1bde3af4 --- /dev/null +++ b/ACE/tests/SSL/Bug_2912_Regression_Test.cpp @@ -0,0 +1,1279 @@ +/** + * @file Bug_2912_Regression_Test.cpp + * + * $Id$ + * + * Reproduces the problems reported in bug 2912: + * http://deuce.doc.wustl.edu/bugzilla/show_bug.cgi?id=2912 + * + * This test reproduces the following interactions: + * + * ACE_TMAIN Client proactor Server proactor + * thread dispatcher thread dispatcher thread + * ---------------- ------------------- -------------------- + * + * init_ssl + * connect + * SH::open SH::open + * SH::read SH::read + * SH::write + * SH::handle_read_stream + * SH::write + * SH::read + * SH::handle_read_stream + * SH::read + * SH::write (causes do_SSL_read to fail) + * + * @author Paul Daugherty <paul@nxicom.com> + */ + +#include "ace/ACE.h" +#include "tests/test_config.h" +#include "ace/SSL/SSL_Asynch_Stream.h" +#include "ace/Proactor.h" +#include "ace/Task.h" +#include "ace/OS.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/Asynch_Connector.h" +#include "ace/Manual_Event.h" + +ACE_RCSID (tests, + Bug_2912_Regression_Test, + "$Id$") + +/* Linux kernels can't hack multiple outstanding I/O, which this + test requires */ +#if defined (ACE_HAS_THREADS) && \ + (defined (ACE_HAS_WIN32_OVERLAPPED_IO) || defined (ACE_HAS_AIO_CALLS)) && \ + !defined (linux) + +/** + * Data payload sent between client and server. Test is not dependent + * on payload characteristics. + */ +#define DATA "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define DATA_SIZE ACE_OS::strlen(DATA) + + +/** + * Client's proactor + * + * The client and server must use different proactors since this bug is + * dependent on threads. + */ +class Client_Proactor : public ACE_Proactor {}; +typedef ACE_Singleton<Client_Proactor, ACE_SYNCH_RECURSIVE_MUTEX> + Client_Proactor_Singleton; +#define CLIENT_PROACTOR Client_Proactor_Singleton::instance() + +class Client_Proactor_Task : public ACE_Task_Base +{ +public: + virtual int svc (void); +}; + +typedef ACE_Singleton<Client_Proactor_Task, ACE_SYNCH_RECURSIVE_MUTEX> + Client_Proactor_Task_Singleton; +#define CLIENT_PROACTOR_TASK Client_Proactor_Task_Singleton::instance() + +int +Client_Proactor_Task::svc (void) +{ + CLIENT_PROACTOR->proactor_reset_event_loop (); + CLIENT_PROACTOR->proactor_run_event_loop (); + return 0; +} + + +/** + * Server's proactor + */ +class Server_Proactor : public ACE_Proactor {}; +typedef ACE_Singleton<Server_Proactor, ACE_SYNCH_RECURSIVE_MUTEX> + Server_Proactor_Singleton; +#define SERVER_PROACTOR Server_Proactor_Singleton::instance () + +class Server_Proactor_Task : public ACE_Task_Base +{ +public: + virtual int svc (void); +}; + +typedef ACE_Singleton<Server_Proactor_Task, ACE_SYNCH_RECURSIVE_MUTEX> + Server_Proactor_Task_Singleton; +#define SERVER_PROACTOR_TASK Server_Proactor_Task_Singleton::instance () + +int +Server_Proactor_Task::svc (void) +{ + SERVER_PROACTOR->proactor_reset_event_loop (); + SERVER_PROACTOR->proactor_run_event_loop (); + return 0; +} + +/** + * This test depends on ADH since the error is related to a missing cert.pem + */ +static DH* dh1024 = 0; + +DH* +get_dh1024 () +{ + static unsigned char dh1024_p[]={ + 0xD7,0xFE,0xEC,0x06,0x28,0x03,0x34,0x96,0xB8,0x08,0x86,0x62, + 0xF1,0xA2,0xBA,0x84,0x7C,0xAF,0xA3,0x1F,0x6A,0x3D,0x03,0x20, + 0x81,0x8D,0x0E,0x43,0x3A,0x54,0x74,0x9F,0x83,0xD2,0xB7,0xE9, + 0x57,0xC1,0x67,0xE9,0x11,0x38,0x2B,0x8E,0x9B,0x1C,0x5D,0x14, + 0x18,0x7D,0x4F,0xEB,0xB1,0x4D,0xFA,0x6F,0x06,0xDD,0xDD,0x6D, + 0x9A,0xD0,0x9E,0x4F,0xE4,0x04,0x3E,0x86,0x6F,0x15,0x60,0x35, + 0x9B,0xA1,0xBA,0x53,0xBA,0x84,0xB5,0x06,0xB1,0xAD,0x94,0x25, + 0xD1,0xED,0xD2,0xF4,0xD7,0x02,0x2F,0x35,0x25,0xE7,0x2D,0x60, + 0xEE,0x7A,0x61,0xAD,0x98,0xA8,0x3D,0xAD,0xB1,0x8A,0x5E,0xCE, + 0xF0,0x09,0xD4,0x67,0x28,0x3D,0x52,0x64,0x78,0xBB,0xC3,0x9D, + 0x40,0xF4,0x72,0xDC,0xC9,0x31,0x0D,0xA3, + }; + static unsigned char dh1024_g[]={ + 0x02, + }; + DH *dh; + + if ((dh=DH_new()) == 0) return(0); + dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),0); + dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),0); + if ((dh->p == 0) || (dh->g == 0)) + { DH_free(dh); return(0); } + return(dh); +} + +DH* +tmp_dh_callback (SSL *s, int is_export, int keylength) +{ + ACE_UNUSED_ARG(s); + ACE_UNUSED_ARG(is_export); + + switch (keylength) { + case 1024: + if (dh1024 == 0) + { + dh1024 = get_dh1024(); + } + return dh1024; + default: + /* Generating a key on the fly is very costly, so use what is there */ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("tmp_dh_callback, unsupported key length, %d\n"), + keylength)); + } + return(0); +} + +bool +init_ssl (void) +{ + SSL_CTX_set_quiet_shutdown (ACE_SSL_Context::instance ()->context(), 1); + SSL_CTX_set_options (ACE_SSL_Context::instance ()->context(), + SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_tmp_dh_callback (ACE_SSL_Context::instance ()->context (), + tmp_dh_callback); + + if (SSL_CTX_set_cipher_list (ACE_SSL_Context::instance ()->context (), "ADH")) + { + return true; + } + else + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("SSL_CTX_set_cipher_list failed\n"))); + return false; + } +} + + +// Function to remove signals from the signal mask. +static int +disable_signal (int sigmin, int sigmax) +{ +#ifndef ACE_WIN32 + + sigset_t signal_set; + if (ACE_OS::sigemptyset (&signal_set) == - 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: (%P|%t):%p\n"), + ACE_TEXT ("sigemptyset failed"))); + + for (int i = sigmin; i <= sigmax; i++) + ACE_OS::sigaddset (&signal_set, i); + + // Put the <signal_set>. + if (ACE_OS::pthread_sigmask (SIG_BLOCK, &signal_set, 0) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Error: (%P|%t):%p\n"), + ACE_TEXT ("pthread_sigmask failed"))); +#else + ACE_UNUSED_ARG (sigmin); + ACE_UNUSED_ARG (sigmax); +#endif /* ACE_WIN32 */ + + return 1; +} + + +/** + * Server's ACE_Service_Handler + */ +class Server_Service_Handler : public ACE_Service_Handler +{ +public: + Server_Service_Handler (void); + + virtual ~Server_Service_Handler (void); + + //FUZZ: disable check_for_lack_ACE_OS + virtual void open (ACE_HANDLE h, ACE_Message_Block&); + //FUZZ: enable check_for_lack_ACE_OS + + virtual void handle_read_stream ( + const ACE_Asynch_Read_Stream::Result &result); + + virtual void handle_write_stream ( + const ACE_Asynch_Write_Stream::Result &result); + + virtual void handle_wakeup (void); + + void cancel_and_close (void); + + int read_data (void); + + int write_data (void); + + //FUZZ: disable check_for_lack_ACE_OS + int read (ACE_Message_Block &mb, size_t bytes_to_read); + + int write (ACE_Message_Block &mb, size_t bytes_to_write); + //FUZZ: enable check_for_lack_ACE_OS + + int safe_to_delete (void) const; + +private: + mutable ACE_SYNCH_RECURSIVE_MUTEX mtx_; + ACE_SSL_Asynch_Stream ssl_stream_; + int pending_writes_; + int pending_reads_; + int handle_wakeup_expected_; + int handle_wakeup_received_; + int closing_; +}; + +Server_Service_Handler::Server_Service_Handler (void) : + ssl_stream_ (ACE_SSL_Asynch_Stream::ST_SERVER), + pending_writes_ (0), + pending_reads_ (0), + handle_wakeup_expected_ (0), + handle_wakeup_received_ (0), + closing_ (0) +{ +} + +Server_Service_Handler::~Server_Service_Handler (void) +{ + if (ACE_INVALID_HANDLE != this->handle ()) + { + ACE_OS::closesocket (this->handle ()); + this->handle (ACE_INVALID_HANDLE); + } +} + +void +Server_Service_Handler::open (ACE_HANDLE h, ACE_Message_Block&) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + if (this->ssl_stream_.open (*this, h, 0, this->proactor ()) != 0) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("Server_Service_Handler::open: ") + ACE_TEXT("ACE_SSL_Asynch_Stream::open failed, %d\n"), + (int)errno)); + //FUZZ: enable check_for_lack_ACE_OS + + this->cancel_and_close (); + } + else + { + if (this->read_data () < 0) + { + this->cancel_and_close (); + } + } +} + +void +Server_Service_Handler::handle_read_stream( + const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->pending_reads_--; + + if (!result.success () || 0 == result.bytes_transferred ()) + { + // Error or remote disconnect + + result.message_block ().release (); + + if (!this->closing_) + { + // No error message when shutting down + + if (!result.success ()) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Server_Service_Handler::handle_read_stream: error: %d\n"), + result.error ())); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Server_Service_Handler::handle_read_stream: remote disconnect\n"))); + } + } + + this->cancel_and_close (); + } + else if (result.bytes_transferred () < result.bytes_to_read ()) + { + // More to read... + + if (this->read (result.message_block (), + result.bytes_to_read () - result.bytes_transferred ()) < 0) + { + result.message_block ().release (); + + this->cancel_and_close (); + } + } + else + { + // Read it all + + result.message_block ().release (); + + // Send data back + + if (this->write_data () < 0) + { + this->cancel_and_close (); + } + + // Next read + + else if (this->read_data () < 0) + { + this->cancel_and_close (); + } + } +} + +void +Server_Service_Handler::handle_write_stream ( + const ACE_Asynch_Write_Stream::Result &result) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->pending_writes_--; + + if (!result.success () || 0 == result.bytes_transferred ()) + { + // Error + + result.message_block ().release (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Server_Service_Handler::handle_write_stream: error: %d\n"), + result.error ())); + + this->cancel_and_close (); + } + else if (result.bytes_transferred () < result.bytes_to_write ()) + { + // More to write... + + if (this->write(result.message_block (), + result.bytes_to_write () - result.bytes_transferred ()) < 0) + { + result.message_block ().release (); + + this->cancel_and_close (); + } + } + else + { + // Wrote it all + + result.message_block().release (); + } +} + +void +Server_Service_Handler::handle_wakeup (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->handle_wakeup_received_ = 1; +} + +void +Server_Service_Handler::cancel_and_close (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->closing_ = 1; + this->ssl_stream_.cancel (); + this->handle_wakeup_expected_ = -1 == this->ssl_stream_.close (); +} + +int +Server_Service_Handler::read_data (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_Message_Block *mb; + ACE_NEW_NORETURN(mb, ACE_Message_Block (DATA_SIZE)); + + int ret = this->read (*mb, DATA_SIZE); + if (ret < 0) + { + mb->release (); + return -1; + } + else + { + return 0; + } +} + +int +Server_Service_Handler::write_data (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_Message_Block *mb; + ACE_NEW_NORETURN(mb, ACE_Message_Block (DATA_SIZE)); + ACE_OS::memcpy (mb->wr_ptr (), DATA, DATA_SIZE); + mb->wr_ptr (DATA_SIZE); + + int ret = this->write (*mb, DATA_SIZE); + if (ret < 0) + { + mb->release (); + return -1; + } + else + { + return 0; + } +} + +int +Server_Service_Handler::read (ACE_Message_Block &mb, size_t bytes_to_read) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + int ret; + if ((ret = this->ssl_stream_.read (mb, bytes_to_read)) < 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Server_Service_Handler::read: read failed: %d\n"), (int)errno)); + } + else + { + this->pending_reads_++; + } + return ret; +} + +int +Server_Service_Handler::write (ACE_Message_Block &mb, size_t bytes_to_write) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + int ret; + if ((ret = this->ssl_stream_.write (mb, bytes_to_write)) < 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Server_Service_Handler::write: write failed: %d\n"), (int)errno)); + } + else + { + this->pending_writes_++; + } + return ret; +} + +int +Server_Service_Handler::safe_to_delete (void) const +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + return 0 == this->pending_writes_ && + 0 == this->pending_reads_ && + (!this->handle_wakeup_expected_ || this->handle_wakeup_received_); +} + +/** + * Server's acceptor + */ +class Acceptor : public ACE_Asynch_Acceptor<Server_Service_Handler> +{ +public: + Acceptor (void); + + virtual ~Acceptor (void); + + virtual int cancel (void); + + virtual int validate_connection (const ACE_Asynch_Accept::Result& result, + const ACE_INET_Addr &remote, const ACE_INET_Addr& local); + + virtual Server_Service_Handler *make_handler (void); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int accept (size_t bytes_to_read = 0, const void *act = 0); + //FUZZ: enable check_for_lack_ACE_OS + + virtual void handle_accept (const ACE_Asynch_Accept::Result &result); + + int safe_to_delete (void) const; + + void prepare_for_connection (Server_Service_Handler *service_handler); + + mutable ACE_SYNCH_RECURSIVE_MUTEX mtx_; + int accept_cnt_; + int cancel_flag_; + Server_Service_Handler *service_handler_; +}; + +typedef ACE_Singleton<Acceptor, ACE_SYNCH_RECURSIVE_MUTEX> Acceptor_Singleton; +#define ACCEPTOR Acceptor_Singleton::instance () + +Acceptor::Acceptor (void) : + accept_cnt_ (0), + cancel_flag_ (0), + service_handler_ (0) +{ +} + +Acceptor::~Acceptor (void) +{ +} + +int +Acceptor::cancel (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->cancel_flag_ = 1; + this->reissue_accept (0); + + ACE_HANDLE h = this->get_handle (); + if (h != ACE_INVALID_HANDLE) + { + this->ACE_Asynch_Acceptor<Server_Service_Handler>::cancel (); + + ACE_OS::closesocket (h); + this->handle (ACE_INVALID_HANDLE); + } + + return 0; +} + +int +Acceptor::safe_to_delete (void) const +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + return (this->cancel_flag_ != 0 && this->accept_cnt_ == 0) ? 1 : 0; +} + +void +Acceptor::prepare_for_connection (Server_Service_Handler *service_handler) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + this->service_handler_ = service_handler; +} + +int +Acceptor::validate_connection (const ACE_Asynch_Accept::Result& result, + const ACE_INET_Addr & /*remote*/, const ACE_INET_Addr& /*local*/) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + if (0 != this->service_handler_ && result.success ()) + { + return 0; + } + else + { + return -1; + } +} + +Server_Service_Handler* +Acceptor::make_handler (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_ASSERT (0 != this->service_handler_); + Server_Service_Handler *service_handler = this->service_handler_; + this->service_handler_ = 0; + return service_handler; +} + +int +Acceptor::accept (size_t bytes_to_read, const void *act) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + if (this->cancel_flag_!= 0) + return -1; + + this->accept_cnt_++; + int rc = this->ACE_Asynch_Acceptor<Server_Service_Handler>::accept ( + bytes_to_read, act); + if (rc != 0) + this->accept_cnt_--; + + return rc; +} + +void +Acceptor::handle_accept (const ACE_Asynch_Accept::Result &result) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->ACE_Asynch_Acceptor<Server_Service_Handler>::handle_accept (result); + + --this->accept_cnt_; +} + + +/** + * Client's ACE_Service_Handler + */ +class Client_Service_Handler : public ACE_Service_Handler +{ +public: + Client_Service_Handler (void); + + virtual ~Client_Service_Handler (void); + + //FUZZ: disable check_for_lack_ACE_OS + virtual void open (ACE_HANDLE h, ACE_Message_Block&); + //FUZZ: enable check_for_lack_ACE_OS + + virtual void handle_read_stream ( + const ACE_Asynch_Read_Stream::Result &result); + + virtual void handle_write_stream ( + const ACE_Asynch_Write_Stream::Result &result); + + virtual void handle_wakeup (void); + + void cancel_and_close (void); + + int read_data (void); + + int write_data (void); + + //FUZZ: disable check_for_lack_ACE_OS + int read (ACE_Message_Block &mb, size_t bytes_to_read); + + int write (ACE_Message_Block &mb, size_t bytes_to_write); + //FUZZ: enable check_for_lack_ACE_OS + + int safe_to_delete (void) const; + + int wait_for_external_write_queue (ACE_Time_Value *wait_time); + + int wait_for_read_completed (ACE_Time_Value *wait_time); + + int read_successful (void) const; + +private: + mutable ACE_SYNCH_RECURSIVE_MUTEX mtx_; + ACE_SSL_Asynch_Stream ssl_stream_; + ACE_Manual_Event ready_for_external_write_; + ACE_Manual_Event read_completed_; + int read_successful_; + int pending_writes_; + int pending_reads_; + int handle_wakeup_expected_; + int handle_wakeup_received_; + int completed_reads_; + int closing_; +}; + +Client_Service_Handler::Client_Service_Handler (void) : + ssl_stream_ (ACE_SSL_Asynch_Stream::ST_CLIENT), + read_successful_ (0), + pending_writes_ (0), + pending_reads_ (0), + handle_wakeup_expected_ (0), + handle_wakeup_received_ (0), + completed_reads_ (0), + closing_ (0) +{ +} + +Client_Service_Handler::~Client_Service_Handler (void) +{ + if (ACE_INVALID_HANDLE != this->handle ()) + { + ACE_OS::closesocket (this->handle ()); + this->handle (ACE_INVALID_HANDLE); + } +} + +void +Client_Service_Handler::open (ACE_HANDLE h, ACE_Message_Block&) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + if (this->ssl_stream_.open (*this, h, 0, this->proactor ()) != 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Client_Service_Handler::open: ACE_SSL_Asynch_Stream::open failed, %d\n"), + (int)errno)); + this->cancel_and_close (); + } + else + { + ACE_Message_Block *mb; + ACE_NEW_NORETURN(mb, ACE_Message_Block (DATA_SIZE)); + + if (this->read_data () < 0 || this->write_data () < 0) + { + this->cancel_and_close (); + } + } +} + +void +Client_Service_Handler::handle_read_stream ( + const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->pending_reads_--; + + if (!result.success () || 0 == result.bytes_transferred ()) + { + // Error or remote disconnect + + result.message_block ().release (); + + if (!this->closing_) + { + // No error message when shutting down + + if (!result.success ()) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Client_Service_Handler::handle_read_stream: error: %d\n"), + result.error ())); + } + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Client_Service_Handler::handle_read_stream: remote disconnect\n"))); + } + } + + this->read_completed_.signal (); + + this->cancel_and_close (); + } + else if (result.bytes_transferred () < result.bytes_to_read ()) + { + // More to read... + + if (this->read (result.message_block(), + result.bytes_to_read () - result.bytes_transferred ()) < 0) + { + result.message_block ().release (); + + this->cancel_and_close (); + } + } + else + { + // Read it all + + this->completed_reads_++; + + result.message_block ().release (); + + // We now have sent and received data in the proactor thread. Signal the + // main thread to try sending data in the main thread. + if (this->completed_reads_ == 1) + { + this->ready_for_external_write_.signal (); + } + else + { + // The main thread wrote data that was echoed back to us on our + // second read. If we get here, the test was successful in that + // the main thread successfully sent data to the server. + this->read_successful_ = 1; + this->read_completed_.signal (); + } + + // Next read + if (this->read_data () < 0) + { + this->cancel_and_close (); + } + } +} + +void +Client_Service_Handler::handle_write_stream ( + const ACE_Asynch_Write_Stream::Result &result) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->pending_writes_--; + + if (!result.success () || 0 == result.bytes_transferred ()) + { + // Error + + result.message_block ().release (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Client_Service_Handler::handle_write_stream: error: %d\n"), + result.error ())); + + this->cancel_and_close (); + } + else if (result.bytes_transferred () < result.bytes_to_write ()) + { + // More to write... + + if (this->write (result.message_block(), + result.bytes_to_write () - result.bytes_transferred ()) < 0) + { + result.message_block ().release (); + + this->cancel_and_close (); + } + } + else + { + // Wrote it all + + result.message_block ().release (); + } +} + +void +Client_Service_Handler::handle_wakeup (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->handle_wakeup_received_ = 1; +} + +void +Client_Service_Handler::cancel_and_close (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->closing_ = 1; + this->ssl_stream_.cancel (); + this->handle_wakeup_expected_ = -1 == this->ssl_stream_.close (); +} + +int +Client_Service_Handler::read_data (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_Message_Block *mb; + ACE_NEW_NORETURN(mb, ACE_Message_Block (DATA_SIZE)); + + int ret = this->read (*mb, DATA_SIZE); + if (ret < 0) + { + mb->release (); + return -1; + } + else + { + return 0; + } +} + +int +Client_Service_Handler::write_data (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_Message_Block *mb; + ACE_NEW_NORETURN (mb, ACE_Message_Block (DATA_SIZE)); + ACE_OS::memcpy (mb->wr_ptr (), DATA, DATA_SIZE); + mb->wr_ptr (DATA_SIZE); + + int ret = this->write (*mb, DATA_SIZE); + if (ret < 0) + { + mb->release (); + return -1; + } + else + { + return 0; + } +} + +int +Client_Service_Handler::read (ACE_Message_Block &mb, size_t bytes_to_read) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + int ret; + if ((ret = this->ssl_stream_.read (mb, bytes_to_read)) < 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Client_Service_Handler::read: read failed: %d\n"), (int)errno)); + } + else + { + this->pending_reads_++; + } + return ret; +} + +int +Client_Service_Handler::write (ACE_Message_Block &mb, size_t bytes_to_write) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + int ret; + if ((ret = this->ssl_stream_.write (mb, bytes_to_write)) < 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Client_Service_Handler::write: write failed: %d\n"), (int)errno)); + } + else + { + this->pending_writes_++; + } + return ret; +} + +int +Client_Service_Handler::safe_to_delete (void) const +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + return 0 == this->pending_writes_ && + 0 == this->pending_reads_ && + (!this->handle_wakeup_expected_ || this->handle_wakeup_received_); +} + +int +Client_Service_Handler::wait_for_external_write_queue (ACE_Time_Value *wait_time) +{ + return this->ready_for_external_write_.wait (wait_time, 0); +} + +int +Client_Service_Handler::wait_for_read_completed (ACE_Time_Value *wait_time) +{ + return this->read_completed_.wait (wait_time, 0); +} + +int +Client_Service_Handler::read_successful (void) const +{ + return this->read_successful_; +} + +/** + * Client's connector + */ +class Connector : public ACE_Asynch_Connector<Client_Service_Handler> +{ +public: + Connector (void); + + virtual ~Connector (void); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int connect ( + const ACE_INET_Addr &remote_sap, + const ACE_INET_Addr &local_sap = + (const ACE_INET_Addr &)ACE_Addr::sap_any, + int reuse_addr = 1, + const void *act = 0); + //FUZZ: enable check_for_lack_ACE_OS + + virtual int validate_connection ( + const ACE_Asynch_Connect::Result& result, + const ACE_INET_Addr &remote, + const ACE_INET_Addr& local); + + int safe_to_delete (void) const; + + void prepare_for_connection (Client_Service_Handler *service_handler); + +protected: + virtual void handle_connect (const ACE_Asynch_Connect::Result &result); + + virtual Client_Service_Handler* make_handler (void); + + mutable ACE_SYNCH_RECURSIVE_MUTEX mtx_; + Client_Service_Handler *service_handler_; + int connecting_; +}; + +typedef ACE_Singleton<Connector, ACE_SYNCH_RECURSIVE_MUTEX> Connector_Singleton; +#define CONNECTOR Connector_Singleton::instance () + +Connector::Connector (void) : + service_handler_ (0) +{ +} + +Connector::~Connector (void) +{ +} + +int +Connector::connect (const ACE_INET_Addr &remote_sap, + const ACE_INET_Addr &local_sap, int reuse_addr, const void *act) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->connecting_ = 1; + + return this->ACE_Asynch_Connector<Client_Service_Handler>::connect ( + remote_sap, local_sap, reuse_addr, act); +} + +int +Connector::validate_connection (const ACE_Asynch_Connect::Result& result, + const ACE_INET_Addr &remote, const ACE_INET_Addr& local) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_UNUSED_ARG (result); + ACE_UNUSED_ARG (remote); + ACE_UNUSED_ARG (local); + + if (!result.success ()) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Connector::validate_connection failed: %d\n"), result.error ())); + return -1; + } + else + { + return 0; + } +} + +void +Connector::handle_connect (const ACE_Asynch_Connect::Result &result) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + this->ACE_Asynch_Connector<Client_Service_Handler>::handle_connect (result); + + this->connecting_ = 0; +} + +Client_Service_Handler* +Connector::make_handler (void) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + ACE_ASSERT(0 != this->service_handler_); + Client_Service_Handler *service_handler = this->service_handler_; + this->service_handler_ = 0; + return service_handler; +} + +int +Connector::safe_to_delete (void) const +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + + return 0 == this->connecting_; +} + +void +Connector::prepare_for_connection (Client_Service_Handler *service_handler) +{ + ACE_Guard<ACE_SYNCH_RECURSIVE_MUTEX> guard (this->mtx_); + this->service_handler_ = service_handler; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2912_Regression_Test")); + + // SSL_CTX_set_cipher_list, etc. + init_ssl (); + + // Keep RT signals on POSIX from killing us. + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + + int ret = 0; + Client_Service_Handler client_handler; + Server_Service_Handler server_handler; + ACE_Time_Value wait_time (10, 0); + + // Client and Server will utilize different proactors since this test + // depends on SSL thread error state behavior. + + CLIENT_PROACTOR_TASK->activate (); + SERVER_PROACTOR_TASK->activate (); + + // Open server acceptor and client connector + + if (0 == ret) + { + ret = ACCEPTOR->open ( + ACE_INET_Addr (ACE_DEFAULT_SERVER_PORT), + 0, + 0, + ACE_DEFAULT_ASYNCH_BACKLOG, + 1, + SERVER_PROACTOR, + 1); + + if (-1 == ret) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "ACE_Asynch_Acceptor::open failed, %d\n"), (int)errno)); + } + } + + if (0 == ret) + { + ret = CONNECTOR->open (0, CLIENT_PROACTOR, 1); + + if (-1 == ret) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "ACE_Asynch_Connector::open failed, %d\n"), (int)errno)); + } + } + + // Supply server_handler and client_handler to acceptor and connector and + // connect client to the server. + + if (0 == ret) + { + ACCEPTOR->prepare_for_connection (&server_handler); + CONNECTOR->prepare_for_connection (&client_handler); + + ret = CONNECTOR->connect ( + ACE_INET_Addr (ACE_DEFAULT_SERVER_PORT, ACE_LOCALHOST)); + + if (-1 == ret) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "ACE_Asynch_Connector::connect failed, %d\n"), (int)errno)); + } + } + + if (0 == ret) + { + ret = client_handler.wait_for_external_write_queue (&wait_time); + if (-1 == ret) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Timed out waiting for client's write readiness\n"))); + } + } + + // Client sends data to server + + if (0 == ret) + { + ret = client_handler.write_data (); + } + + // Client waits for echo reply from server + + if (0 == ret) + { + ret = client_handler.wait_for_read_completed (&wait_time); + + if (-1 == ret) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ( + "Timed out waiting for client's read to complete\n"))); + } + } + + if (0 == ret) + { + if (client_handler.read_successful () == 1) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Success\n"))); + ret = 0; + } + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Client's read failed\n"))); + ret = -1; + } + } + + // Cleanup and shutdown + + ACCEPTOR->cancel (); + while (!ACCEPTOR->safe_to_delete ()) + ACE_OS::sleep (ACE_Time_Value (0, 500000)); + + CONNECTOR->cancel (); + while (!CONNECTOR->safe_to_delete ()) + ACE_OS::sleep (ACE_Time_Value (0, 500000)); + + client_handler.cancel_and_close (); + while (!client_handler.safe_to_delete ()) + ACE_OS::sleep (ACE_Time_Value (0, 500000)); + + server_handler.cancel_and_close (); + while (!server_handler.safe_to_delete ()) + ACE_OS::sleep (ACE_Time_Value (0, 500000)); + + CLIENT_PROACTOR->proactor_end_event_loop (); + CLIENT_PROACTOR_TASK->wait (); + + SERVER_PROACTOR->proactor_end_event_loop (); + SERVER_PROACTOR_TASK->wait (); + + ACE_END_TEST; + + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Bug_2912_Regression_Test")); + + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("threads or proactor not supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS && (ACE_HAS_WIN32_OVERLAPPED_IO || ACE_HAS_AIO_CALLS) && !linux */ diff --git a/ACE/tests/SSL/Main.cpp b/ACE/tests/SSL/Main.cpp new file mode 100644 index 00000000000..44b92128394 --- /dev/null +++ b/ACE/tests/SSL/Main.cpp @@ -0,0 +1,30 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Main.cpp +// +// = DESCRIPTION +// This is a wrapper for the test programs. It obviates the test cpp's +// from having to always include OS.h. +// +// = AUTHOR +// Don Hinton <dhinton@dresystems.com> +// +// ============================================================================ + +#include "ace/OS_main.h" +#if defined (ACE_HAS_WINCE) +# include "ace/ACE.h" +#endif /* ACE_HAS_WINCE */ +int run_main (int argc, ACE_TCHAR *argv[]); + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + return run_main (argc, argv); +} diff --git a/ACE/tests/SSL/Makefile.am b/ACE/tests/SSL/Makefile.am new file mode 100644 index 00000000000..ee10293e8ca --- /dev/null +++ b/ACE/tests/SSL/Makefile.am @@ -0,0 +1,79 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## ./bin/mwc.pl -type automake -noreldefs ACE.mwc + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +noinst_PROGRAMS = + +## Makefile.SSL_Asynch_Stream_Test.am + +if BUILD_SSL +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SSL_Asynch_Stream_Test + +SSL_Asynch_Stream_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_HAS_SSL=1 \ + @ACE_TLS_CPPFLAGS@ + +SSL_Asynch_Stream_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + SSL_Asynch_Stream_Test.cpp + +SSL_Asynch_Stream_Test_LDFLAGS = \ + @ACE_TLS_LDFLAGS@ + +SSL_Asynch_Stream_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/SSL/libACE_SSL.la \ + $(top_builddir)/tests/libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + @ACE_TLS_LIBS@ + +endif !BUILD_ACE_FOR_TAO +endif BUILD_SSL + +## Makefile.Thread_Pool_Reactor_SSL_Test.am + +if BUILD_SSL + +noinst_PROGRAMS += Thread_Pool_Reactor_SSL_Test + +Thread_Pool_Reactor_SSL_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_HAS_SSL=1 \ + @ACE_TLS_CPPFLAGS@ + +Thread_Pool_Reactor_SSL_Test_SOURCES = \ + $(ACE_ROOT)/tests/Main.cpp \ + Thread_Pool_Reactor_SSL_Test.cpp \ + Thread_Pool_Reactor_SSL_Test.h + +Thread_Pool_Reactor_SSL_Test_LDFLAGS = \ + @ACE_TLS_LDFLAGS@ + +Thread_Pool_Reactor_SSL_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/SSL/libACE_SSL.la \ + $(top_builddir)/tests/libTest_Output.la \ + $(ACE_BUILDDIR)/ace/libACE.la \ + @ACE_TLS_LIBS@ + +endif BUILD_SSL + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/tests/SSL/SSL_Asynch_Stream_Test.cpp b/ACE/tests/SSL/SSL_Asynch_Stream_Test.cpp new file mode 100644 index 00000000000..dc07b703530 --- /dev/null +++ b/ACE/tests/SSL/SSL_Asynch_Stream_Test.cpp @@ -0,0 +1,486 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests/SSL +// +// = FILENAME +// SSL_Asynch_Stream_Test.cpp +// +// = DESCRIPTION +// This program is a functionality test of ACE_SSL_Asynch_Stream. +// It demonstrates one proper use case of ACE_SSL_Asynch_Stream in the +// Proactor framework and validates its basic functionality. +// +// Usage: SSL_Asynch_Stream_Test [-r <hostname:port#>] +// [-t <num threads>] [-d <delay>] +// [-i <client conn attempt#>] [-n <client request# per conn>] +// +// Default value: +// <hostname:port#>: ACE_DEFAULT_SERVER_HOST:ACE_DEFAULT_PORT +// <num threads>: ACE_MAX_THREADS +// <client conn attempt#>: ACE_MAX_ITERATIONS +// <client req# per conn>: 20 +// <delay>: 0 usec +// +// = AUTHOR +// Steve Huston <shuston@riverace.com> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Default_Constants.h" +#include "ace/OS_NS_string.h" +#include "ace/Event_Handler.h" +#include "ace/Get_Opt.h" +#include "ace/Proactor.h" +#include "ace/Reactor.h" +#include "ace/Thread_Manager.h" +#include "ace/INET_Addr.h" +#include "ace/SSL/SSL_Asynch_Stream.h" +#include "ace/SSL/SSL_SOCK_Connector.h" +#include "ace/SSL/SSL_SOCK_Acceptor.h" +#include "ace/SSL/SSL_SOCK_Stream.h" + +ACE_RCSID(tests, SSL_Asynch_Stream_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) && ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + // This only works on Win32 platforms and on Unix platforms + // supporting POSIX aio calls. + +class Client_Handler : public ACE_Handler +{ +public: + Client_Handler () + : msgs_sent_ (0), + stream_ (ACE_SSL_Asynch_Stream::ST_CLIENT), + block_ (1024) {} + ~Client_Handler (); + + //FUZZ: disable check_for_lack_ACE_OS + int open (ACE_HANDLE); + //FUZZ: enable check_for_lack_ACE_OS + +private: + virtual void handle_write_stream (const ACE_SSL_Asynch_Write_Stream_Result &result); + +private: + size_t msgs_sent_; + ACE_SSL_Asynch_Stream stream_; + ACE_Message_Block block_; +}; + +class Server_Handler : public ACE_Handler +{ +public: + Server_Handler () + : msgs_rcvd_ (0), + stream_ (ACE_SSL_Asynch_Stream::ST_SERVER), + block_ (1024) {} + ~Server_Handler (); + + //FUZZ: disable check_for_lack_ACE_OS + int open (ACE_HANDLE); + //FUZZ: enable check_for_lack_ACE_OS + +private: + virtual void handle_read_stream (const ACE_SSL_Asynch_Read_Stream_Result &result); + +private: + size_t msgs_rcvd_; + ACE_SSL_Asynch_Stream stream_; + ACE_Message_Block block_; +}; + +class Server_Acceptor : public ACE_Event_Handler +{ +public: + //FUZZ: disable check_for_lack_ACE_OS + int open (const ACE_INET_Addr &listen_addr); + //FUZZ: enable check_for_lack_ACE_OS + + // Called when a new connection is ready to accept. + virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE); + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + +private: + ACE_SSL_SOCK_Acceptor acceptor_; +}; + +// Accepting end point. This is actually "localhost:10010", but some +// platform couldn't resolve the name so we use the IP address +// directly here. +static const ACE_TCHAR *rendezvous = \ + ACE_DEFAULT_SERVER_HOST ACE_TEXT (":") ACE_DEFAULT_SERVER_PORT_STR; + +// Total number of proactor threads. +static size_t num_threads = ACE_MAX_THREADS; + +#if defined (CHORUS) // Add platforms that can't handle too many + // connection simultaneously here. +#define ACE_LOAD_FACTOR /2 +#else +#define ACE_LOAD_FACTOR +#endif + +// Number of client connections to attempt. +static size_t cli_conn_no = ACE_MAX_ITERATIONS ACE_LOAD_FACTOR; + +// Number of requests each client connection sends. +static size_t cli_req_no = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Delay before a thread sending the next request (in msec.) +static int req_delay = 0; + +// This is the string sent from client to server. +static const char *test_string = "SSL_Asynch_Stream_Test!"; + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("r:t:d:i:n:")); + + int c; + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 'r': // hostname:port + rendezvous = getopt.opt_arg (); + break; + case 't': + num_threads = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'd': + req_delay = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'i': + cli_conn_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'n': + cli_req_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Usage: %s [-r <hostname:port#>]") + ACE_TEXT ("\t[-t <nr threads>] [-d <delay>]") + ACE_TEXT ("\t[-i <client conn attempt#>]") + ACE_TEXT ("\t[-n <client request# per conn>]\n"), + argv[0])); + break; + } + } +} + +Client_Handler::~Client_Handler () +{ + if (this->stream_.handle () != ACE_INVALID_HANDLE) + { + if (this->msgs_sent_ != cli_req_no) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Client handle %d sent %d messages; ") + ACE_TEXT ("expected %d\n"), + this->stream_.handle (), + this->msgs_sent_, + cli_req_no)); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client handle %d sent %d messages; ") + ACE_TEXT ("closing connection\n"), + this->stream_.handle (), + cli_req_no)); + } + this->stream_.close (); +} + +int +Client_Handler::open (ACE_HANDLE handle) +{ + if (this->stream_.open (*this, handle) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Client_Handler: %p\n"), + ACE_TEXT ("open")), + -1); + this->block_.copy (test_string); + if (this->stream_.write (this->block_, this->block_.length ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Client_Handler: %p\n"), + ACE_TEXT ("initiate write")), + -1); + return 0; +} + +void +Client_Handler::handle_write_stream + (const ACE_SSL_Asynch_Write_Stream_Result &result) +{ + if (!result.success ()) + { + errno = result.error (); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Client handle %d: %p\n"), + this->stream_.handle (), + ACE_TEXT ("write"))); + delete this; + return; + } + ACE_Message_Block &b = result.message_block (); + bool send_again = true; + if (b.length () == 0) + { + // All block's data sent; rewind the read pointer and send it again + // until we've sent the configured number of times. + ++this->msgs_sent_; + if (this->msgs_sent_ == cli_req_no) + send_again = false; // All done + else + b.rd_ptr (b.base ()); + } + + if (send_again) + { + if (this->stream_.write (this->block_, this->block_.length ()) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Client_Handler: %p\n"), + ACE_TEXT ("initiate write"))); + delete this; + } + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Client handle %d done sending\n"), + this->stream_.handle ())); + delete this; + } + return; +} + +Server_Handler::~Server_Handler () +{ + if (this->stream_.handle () != ACE_INVALID_HANDLE) + { + if (this->msgs_rcvd_ != cli_req_no) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Server handle %d received %d messages; ") + ACE_TEXT ("expected %d\n"), + this->stream_.handle (), + this->msgs_rcvd_, + cli_req_no)); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server handle %d received %d messages; ") + ACE_TEXT ("closing connection\n"), + this->stream_.handle (), + cli_req_no)); + } + this->stream_.close (); +} + +int +Server_Handler::open (ACE_HANDLE handle) +{ + if (this->stream_.open (*this, handle) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Server_Handler: %p\n"), + ACE_TEXT ("open")), + -1); + if (this->stream_.read (this->block_, this->block_.space () - 1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) Server_Handler: %p\n"), + ACE_TEXT ("read")), + -1); + return 0; +} + +void +Server_Handler::handle_read_stream + (const ACE_SSL_Asynch_Read_Stream_Result &result) +{ + if (!result.success ()) + { + errno = result.error (); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Server handle %d: %p\n"), + this->stream_.handle (), + ACE_TEXT ("read"))); + delete this; + return; + } + if (result.bytes_transferred () == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Server handle %d closed by peer\n"), + this->stream_.handle ())); + delete this; + return; + } + + // Scan through the received data for the expected string. There may be + // multiples and/or partials. Count up how many arrive before the connection + // is closed. + // The read operation left one byte space at the end so we can insert a + // nul terminator to ease scanning. + ACE_Message_Block &b = result.message_block (); + *(b.wr_ptr ()) = '\0'; + size_t test_string_len = ACE_OS::strlen (test_string); + while (b.length () >= test_string_len) + { + if (0 != ACE_OS::strncmp (b.rd_ptr (), test_string, test_string_len)) + ACE_ERROR_BREAK ((LM_ERROR, + ACE_TEXT ("(%t) Read string: %C; expected: %C\n"), + b.rd_ptr (), + test_string)); + b.rd_ptr (test_string_len); + } + b.crunch (); + if (this->stream_.read (b, b.space () - 1) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) Server_Handler: %p\n"), + ACE_TEXT ("read"))); + delete this; + } + return; +} + + +int +Server_Acceptor::open (const ACE_INET_Addr &listen_addr) +{ + if (this->acceptor_.open (listen_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("listen")), + -1); + return 0; +} + +int +Server_Acceptor::handle_input (ACE_HANDLE) +{ + ACE_SSL_SOCK_Stream new_stream; + if (this->acceptor_.accept (new_stream) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("accept")), + -1); + Server_Handler *new_handler = 0; + ACE_NEW_RETURN (new_handler, Server_Handler, -1); + if (new_handler->open (new_stream.get_handle ()) != 0) + delete new_handler; + + return 0; +} + +int +Server_Acceptor::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + this->acceptor_.close (); + return 0; +} + + +static ACE_THR_FUNC_RETURN +proactor_loop (void *) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Start handling events.\n"))); + + int result = + ACE_Proactor::instance ()->proactor_run_event_loop (); + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Error handling events")), + 0); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Done handling events.\n"))); + + return 0; +} + +static ACE_THR_FUNC_RETURN +start_clients (void *) +{ + // Client thread function. + ACE_INET_Addr addr (rendezvous); + ACE_SSL_SOCK_Stream stream; + ACE_SSL_SOCK_Connector connect; + + for (size_t i = 0 ; i < cli_conn_no; i++) + { + if (connect.connect (stream, addr) < 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("connect"))); + continue; + } + + Client_Handler *new_handler = 0; + ACE_NEW_RETURN (new_handler, Client_Handler, (ACE_THR_FUNC_RETURN)-1); + if (new_handler->open (stream.get_handle ()) != 0) + delete new_handler; + stream.set_handle (ACE_INVALID_HANDLE); + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("SSL_Asynch_Stream_Test")); + + ACE_SSL_Context *context = ACE_SSL_Context::instance (); + // Note - the next two strings are naked on purpose... the arguments to + // the ACE_SSL_Context methods are const char *, not ACE_TCHAR *. + context->certificate ("dummy.pem", SSL_FILETYPE_PEM); + context->private_key ("key.pem", SSL_FILETYPE_PEM); + + parse_args (argc, argv); + + Server_Acceptor acceptor; + ACE_INET_Addr accept_addr (rendezvous); + + if (acceptor.open (accept_addr) == -1) + return 1; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Listening at %s\n"), rendezvous)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) Spawning %d proactor threads\n"), + num_threads)); + ACE_Thread_Manager::instance ()->spawn_n (num_threads, proactor_loop); + ACE_Thread_Manager::instance ()->spawn (start_clients); + + ACE_Time_Value loop_limit (20); + ACE_Reactor::instance ()->run_reactor_event_loop (loop_limit); + ACE_Thread_Manager::instance ()->wait (); + + // Check for num connections up/down. + + ACE_END_TEST; + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SSL_Asynch_Stream_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("This test requires threads and AIO which are not ") + ACE_TEXT ("supported on this platform\n"))); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS && (WIN32 || AIO) */ diff --git a/ACE/tests/SSL/Thread_Pool_Reactor_SSL_Test.cpp b/ACE/tests/SSL/Thread_Pool_Reactor_SSL_Test.cpp new file mode 100644 index 00000000000..7f9bbd6c168 --- /dev/null +++ b/ACE/tests/SSL/Thread_Pool_Reactor_SSL_Test.cpp @@ -0,0 +1,354 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests/SSL +// +// = FILENAME +// Thread_Pool_Reactor_Test.cpp +// +// = DESCRIPTION +// This program is a torture test of threaded SSL usage. It +// is based on the tests/Thread_Pool_Reactor_Test and adds +// SSL stuff submitted by Robert Handl <robert.handl@ehpt.com>. +// It starts by spawning several server threads waiting to handle +// events. Several other client threads are spawned right after +// to initiate connections to server threads. Each connection +// adds a new Svc_Handler into the TP_Reactor and sends out +// several "requests" to the server thread. After the connection +// is closed, the Svc_Handler is removed from the TP_Reactor. +// Each message is treated as a separate request by the server so +// two consecutive requests might be serviced by two different +// threads. +// +// Usage: Thread_Pool_Reactor_Test_SSL [-r <hostname:port#>] +// [-s <server thr#>] [-c <client thr#>] [-d <delay>] +// [-i <client conn attempt#>] [-n <client request# per conn>] +// +// Default value: +// <hostname:port#>: ACE_DEFAULT_RENDEZVOUS +// <server thr#>: ACE_MAX_THREADS +// <client thr#>: ACE_MAX_ITERATIONS +// <client conn attempt#>: ACE_MAX_ITERATIONS +// <client req# per conn>: ACE_MAX_THREADS +// <delay>: 50 usec +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> and +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Get_Opt.h" +#include "ace/Acceptor.h" +#include "ace/Thread_Manager.h" +#include "ace/TP_Reactor.h" +#include "ace/SSL/SSL_SOCK_Connector.h" +#include "ace/SSL/SSL_SOCK_Acceptor.h" + +ACE_RCSID(tests, Atomic_Op_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#include "Thread_Pool_Reactor_SSL_Test.h" +typedef ACE_Strategy_Acceptor <Request_Handler, ACE_SSL_SOCK_ACCEPTOR> + ACCEPTOR; + +// Accepting end point. This is actually "localhost:10010", but some +// platform couldn't resolve the name so we use the IP address +// directly here. +static const ACE_TCHAR *rendezvous = ACE_TEXT ("127.0.0.1:10010"); + +// Total number of server threads. +static size_t svr_thrno = ACE_MAX_THREADS; + +#if defined (CHORUS) // Add platforms that can't handle too many + // connection simultaneously here. +#define ACE_LOAD_FACTOR /2 +#else +#define ACE_LOAD_FACTOR +#endif + +// Total number of client threads. +static size_t cli_thrno = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Total connection attemps of a client thread. +static size_t cli_conn_no = ACE_MAX_ITERATIONS ACE_LOAD_FACTOR; + +// Total requests a client thread sends. +static size_t cli_req_no = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Delay before a thread sending the next request (in msec.) +static int req_delay = 50; + +static void +parse_arg (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("r:s:c:d:i:n:")); + + int c; + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 'r': // hostname:port + rendezvous = getopt.opt_arg (); + break; + case 's': + svr_thrno = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'c': + cli_thrno = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'd': + req_delay = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'i': + cli_conn_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'n': + cli_req_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, + "Usage: Thread_Pool_Reactor_Test [-r <hostname:port#>]" + "\t[-s <server thr#>] [-c <client thr#>] [-d <delay>]" + "\t[-i <client conn attempt#>]" + "[-n <client request# per conn>]\n")); + break; + } + } +} + +Request_Handler::Request_Handler (ACE_Thread_Manager *thr_mgr) + : ACE_Svc_Handler<ACE_SSL_SOCK_STREAM, ACE_MT_SYNCH> (thr_mgr), + nr_msgs_rcvd_(0) +{ + // Make sure we use TP_Reactor with this class (that's the whole + // point, right?) + this->reactor (ACE_Reactor::instance ()); +} + +int +Request_Handler::handle_input (ACE_HANDLE fd) +{ + ACE_TCHAR buffer[BUFSIZ]; + ACE_TCHAR len = 0; + ssize_t result = this->peer ().recv (&len, sizeof (ACE_TCHAR)); + + if (result > 0 + && this->peer ().recv_n (buffer, len * sizeof (ACE_TCHAR)) + == static_cast<ssize_t> (len * sizeof (ACE_TCHAR))) + { + ++this->nr_msgs_rcvd_; + + ACE_DEBUG ((LM_DEBUG, + "(%t) svr input; fd: 0x%x; input: %s\n", + fd, + buffer)); + if (ACE_OS::strcmp (buffer, ACE_TEXT ("shutdown")) == 0) + ACE_Reactor::end_event_loop (); + return 0; + } + else + ACE_DEBUG ((LM_DEBUG, + "(%t) Request_Handler: 0x%x peer closed (0x%x)\n", + this, fd)); + return -1; +} + +int +Request_Handler::handle_close (ACE_HANDLE fd, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) svr close; fd: 0x%x, rcvd %d msgs\n", + fd, + this->nr_msgs_rcvd_)); + if (this->nr_msgs_rcvd_ != cli_req_no) + ACE_ERROR((LM_ERROR, + "(%t) Handler 0x%x: Expected %d messages; got %d\n", + this, + cli_req_no, + this->nr_msgs_rcvd_)); + this->destroy (); + return 0; +} + +static int +reactor_event_hook (ACE_Reactor *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) handling events ....\n")); + + return 0; +} + +static ACE_THR_FUNC_RETURN +svr_worker (void *) +{ + // Server thread function. + int result = + ACE_Reactor::instance ()->run_reactor_event_loop (&reactor_event_hook); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) %p\n", + "Error handling events"), + 0); + + ACE_DEBUG ((LM_DEBUG, + "(%t) I am done handling events. Bye, bye\n")); + + return 0; +} + +static ACE_THR_FUNC_RETURN +cli_worker (void *arg) +{ + // Client thread function. + ACE_INET_Addr addr (rendezvous); + ACE_SSL_SOCK_Stream stream; + ACE_SSL_SOCK_Connector connect; + ACE_Time_Value delay (0, req_delay); + size_t len = * reinterpret_cast<ACE_TCHAR *> (arg); + + for (size_t i = 0 ; i < cli_conn_no; i++) + { + if (connect.connect (stream, addr) < 0) + { + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "connect")); + continue; + } + + for (size_t j = 0; j < cli_req_no; j++) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) conn_worker handle 0x%x, req %d\n", + stream.get_handle (), + j+1)); + if (stream.send_n (arg, + (len + 1) * sizeof (ACE_TCHAR)) == -1) + { + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "send_n")); + continue; + } + ACE_OS::sleep (delay); + } + + stream.close (); + } + + return 0; +} + +static ACE_THR_FUNC_RETURN +worker (void *) +{ + ACE_OS::sleep (3); + const ACE_TCHAR *msg = ACE_TEXT ("Message from Connection worker"); + ACE_TCHAR buf [BUFSIZ]; + buf[0] = ACE_OS::strlen (msg) + 1; + ACE_OS::strcpy (&buf[1], msg); + + ACE_INET_Addr addr (rendezvous); + + ACE_DEBUG((LM_DEBUG, + "(%t) Spawning %d client threads...\n", + cli_thrno)); + int grp = ACE_Thread_Manager::instance ()->spawn_n (cli_thrno, + &cli_worker, + buf); + ACE_ASSERT (grp != -1); + + ACE_Thread_Manager::instance ()->wait_grp (grp); + + ACE_DEBUG ((LM_DEBUG, + "(%t) Client threads done; shutting down...\n")); + ACE_SSL_SOCK_Stream stream; + ACE_SSL_SOCK_Connector connect; + + if (connect.connect (stream, addr) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p Error while connecting\n", + "connect")); + + const ACE_TCHAR *sbuf = ACE_TEXT ("\011shutdown"); + + ACE_DEBUG ((LM_DEBUG, + "shutdown stream handle = %x\n", + stream.get_handle ())); + + if (stream.send_n (sbuf, (ACE_OS::strlen (sbuf) + 1) * sizeof (ACE_TCHAR)) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "send_n")); + + stream.close (); + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Reactor_SSL_Test")); + + ACE_SSL_Context *context = ACE_SSL_Context::instance (); + // Note - the next two strings are naked on purpose... the arguments to + // the ACE_SSL_Context methods are const char *, not ACE_TCHAR *. + context->certificate ("dummy.pem", SSL_FILETYPE_PEM); + context->private_key ("key.pem", SSL_FILETYPE_PEM); + + parse_arg (argc, argv); + + // Changed the default + ACE_TP_Reactor sr; + ACE_Reactor new_reactor (&sr); + ACE_Reactor::instance (&new_reactor); + + ACCEPTOR acceptor; + ACE_INET_Addr accept_addr (rendezvous); + + if (acceptor.open (accept_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 1); + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("(%t) Spawning %d server threads...\n"), + svr_thrno)); + ACE_Thread_Manager::instance ()->spawn_n (svr_thrno, + svr_worker); + ACE_Thread_Manager::instance ()->spawn (worker); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_END_TEST; + return 0; +} + +#else +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Reactor_SSL_Test")); + + ACE_ERROR ((LM_INFO, + "threads not supported on this platform\n")); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/SSL/Thread_Pool_Reactor_SSL_Test.h b/ACE/tests/SSL/Thread_Pool_Reactor_SSL_Test.h new file mode 100644 index 00000000000..fca5a7f8432 --- /dev/null +++ b/ACE/tests/SSL/Thread_Pool_Reactor_SSL_Test.h @@ -0,0 +1,50 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests/SSL +// +// = FILENAME +// Thread_Pool_Reactor_Test_SSL.h +// +// = DESCRIPTION +// This class gets its own header file to work around AIX C++ +// compiler "features" related to template instantiation... It is +// only used by Thread_Pool_Reactor_Test_SSL.cpp. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_THREAD_POOL_REACTOR_TEST_SSL_H +#define ACE_TESTS_THREAD_POOL_REACTOR_TEST_SSL_H + +#include "ace/SSL/SSL_SOCK_Stream.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Svc_Handler.h" + +class Request_Handler : public ACE_Svc_Handler<ACE_SSL_SOCK_STREAM, + ACE_MT_SYNCH> +{ + // = TITLE + // This class is the Svc_Handler used by <Acceptor>. +public: + Request_Handler (ACE_Thread_Manager *tm = 0); + // The default constructor makes sure the right reactor is used. + +protected: + virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE); + virtual int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask = 0); + +private: + size_t nr_msgs_rcvd_; +}; + +#endif /* ACE_TESTS_THREAD_POOL_REACTOR_TEST_SSL_H */ diff --git a/ACE/tests/SSL/acetest.mpb b/ACE/tests/SSL/acetest.mpb new file mode 100644 index 00000000000..d7ef35a7061 --- /dev/null +++ b/ACE/tests/SSL/acetest.mpb @@ -0,0 +1,21 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + + after += Test_Output + libs += Test_Output + libpaths += .. + + Source_Files { + ../Main.cpp + } + Header_Files { + } + Resource_Files { + } + Documentation_Files { + } + Inline_Files { + } +} diff --git a/ACE/tests/SSL/dummy.pem b/ACE/tests/SSL/dummy.pem new file mode 100644 index 00000000000..d631a33b956 --- /dev/null +++ b/ACE/tests/SSL/dummy.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICaTCCAdICAQAwDQYJKoZIhvcNAQEEBQAwcjELMAkGA1UEBhMCVVMxCzAJBgNV +BAgTAkNBMQ8wDQYDVQQHEwZJcnZpbmUxDDAKBgNVBAoTA09DSTEMMAoGA1UECxMD +VEFPMREwDwYDVQQDEwhwcml5YW5rYTEWMBQGCSqGSIb3DQEJARYHcGdvbnRsYTAe +Fw0wMTAzMjkwNDM4NDZaFw0wMTA0MjgwNDM4NDZaMIGHMQswCQYDVQQGEwJVUzEL +MAkGA1UECBMCQ0ExDzANBgNVBAcTBklydmluZTEdMBsGA1UEChMUT2JqZWN0IENv +bXB1dGluZyBJbmMxEDAOBgNVBAsTB09DSStUQU8xETAPBgNVBAMTCHByaXlhbmth +MRYwFAYJKoZIhvcNAQkBFgdwZ29udGxhMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQClC6z/bX1JHF1Hg06NCnBmsikEjViEdJFuqLOH3rXSGbm+2Eo+IO4dHlFS +u6+Ntk4olBZTuf0DqzyEgrOiN7cnKXpxJzb1cwCmVkvDQISMygf4o66+CHtF8o8Z +Sbi9F5u9W+MILaoCexEIVZqfHffcGxvm5O2MorBSQNka3NcC3wIDAQABMA0GCSqG +SIb3DQEBBAUAA4GBADuKX6kllE2sNdQYMbSzt5C/lcpgcsK0BR6L01cQA95b5TvL +HsKMeeeRj2npR4EPXY2gqgWTrKHZvf01aoKE5LuyzSQ+qfFMuEfo7+p9SYIuIrLD +5+J0wOwN0R0YZAEY5gCAqRGw26dwWDai+PASPsx0YXV1y9jBB1FFtUFgrpR8 +-----END CERTIFICATE----- diff --git a/ACE/tests/SSL/key.pem b/ACE/tests/SSL/key.pem new file mode 100644 index 00000000000..54ff8f0f488 --- /dev/null +++ b/ACE/tests/SSL/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQClC6z/bX1JHF1Hg06NCnBmsikEjViEdJFuqLOH3rXSGbm+2Eo+ +IO4dHlFSu6+Ntk4olBZTuf0DqzyEgrOiN7cnKXpxJzb1cwCmVkvDQISMygf4o66+ +CHtF8o8ZSbi9F5u9W+MILaoCexEIVZqfHffcGxvm5O2MorBSQNka3NcC3wIDAQAB +AoGALYq/PexUeewdwTH2ZuzOf0gCEYN/PW19A/ABOii2OzdmDcdZFTO5AMfw4Mdx +dcUsY/4Y+xmDO5Pwqw/1yXleTDqvEKCgIEHN4NWnYYSiZOy3LBzQ8XaMZ7/2PCqc +s4EtesuRB2kZ7PH2R1vJfyGIxZPaO5MOFbs3QFnpBUjqOmECQQDQCYgnBcshCEro +gsrTjxtZiVHjmXEo0Uo2m4CBQW1PLJmmUXBzivGkVFfhjKULjwvso3BePfmzy9wP +7YFjVXN9AkEAyxjBXi2kYCcXfGQiNuIrLkyVXeGR2YWnhzS2nL1TjNngmCBbnj48 +qvoqOUQgFK0AeTe/x7lb4Cf24ejWF5vmiwJALensorAkpKWv4qD7IrXy00/7QsAa +uWd3eZXYRq6p8U9mmc5fgyCnNB1pR95CjsqDVza7FhGXipbzepBwffveAQJBAMKc +mxYyqDMW4nNoxDxRJs17xxkpwAdvAiQWB/JTnQ737DX5s7EDtECl7PXo6NDHIhAF +srigToCR6wl4gkYnNpcCQHmlfa9Duf3VJI/XeHE9ZU8vS4qgx0Ikfh01xCqWlsaq +nPRmtfktt4P8gxlryZCEPpRs9l/XwQY6tnpHr5EmV2Y= +-----END RSA PRIVATE KEY----- diff --git a/ACE/tests/SSL/tests.mpc b/ACE/tests/SSL/tests.mpc new file mode 100644 index 00000000000..5b881ce28b7 --- /dev/null +++ b/ACE/tests/SSL/tests.mpc @@ -0,0 +1,26 @@ +// -*- MPC -*- +// $Id$ + +project(Bug_2912_Regression_Test) : acetest, ssl { + avoids += ace_for_tao + exename = Bug_2912_Regression_Test + Source_Files { + Bug_2912_Regression_Test.cpp + } +} + +project(SSL Asynch_Stream Test) : acetest, ssl { + avoids += ace_for_tao + exename = SSL_Asynch_Stream_Test + Source_Files { + SSL_Asynch_Stream_Test.cpp + } +} + +project(Thread Pool Reactor SSL Test) : acetest, ssl { + exename = Thread_Pool_Reactor_SSL_Test + Source_Files { + Thread_Pool_Reactor_SSL_Test.cpp + } +} + diff --git a/ACE/tests/SString_Test.cpp b/ACE/tests/SString_Test.cpp new file mode 100644 index 00000000000..f91e2e8e9ed --- /dev/null +++ b/ACE/tests/SString_Test.cpp @@ -0,0 +1,345 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// SString_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the use of ACE_CString +// and ACE_WString. No command line arguments are needed to run +// the test. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/Auto_Ptr.h" +#include "ace/SString.h" + +ACE_RCSID(tests, SString_Test, "$Id$") + +static int testConcatenation() { +#ifdef ACE_HAS_WCHAR + ACE_WString s1; + s1 += L'H'; + if (s1 != ACE_WString(L"H")) { + ACE_ERROR((LM_ERROR, "Concat wchar_t\n")); + return 1; + } + s1 = ACE_WString(L"Hello"); + s1 += L" World"; + if (s1 != ACE_WString(L"Hello World")) { + ACE_ERROR((LM_ERROR, "Concat wchar_t*\n")); + return 1; + } + s1 = L"Hello"; + s1 += ACE_WString(L" World"); + if (s1 != ACE_WString(L"Hello World")) { + ACE_ERROR((LM_ERROR, "Concat wstring\n")); + return 1; + } + s1 = L"Hello"; + s1.append(L" World", 6); + if (s1 != ACE_WString(L"Hello World")) { + ACE_ERROR((LM_ERROR, "Concat wchar_t* 2\n")); + return 1; + } + s1 += L'.'; + if (s1 != ACE_WString(L"Hello World.")) { + ACE_ERROR((LM_ERROR, "Concat wchar_t\n")); + return 1; + } + ACE_WString s2(L"Hello World"); + s2 += L'.'; + if (s2 != ACE_WString(L"Hello World.")) { + ACE_ERROR((LM_ERROR, "Concat wchar_t 2\n")); + return 1; + } +#endif /* ACE_HAS_WCHAR */ + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SString_Test")); + + { + + /* Set #1 */ + ACE_CString s0 ("hello"); + ACE_CString s1 ("hello"); + ACE_CString s2 ("world"); + ACE_CString s3 ("ll"); + ACE_CString s4 ("ello"); + ACE_CString s5 = s1 + " " + s2; + + char single_character = 'z'; + ACE_CString single_character_string (single_character); + + ACE_CString empty_string; + ACE_CString zero_size_string (s1.c_str (), 0, 0, 1); + + if (ACE_CString::npos == 0) + ACE_ERROR((LM_ERROR,"Set #1: npos is incorrect.\n")); + + // Not equal comparisons. Error if they are equal + if (s1 == s2){ACE_ERROR((LM_ERROR,"Set #1:\n"));return 1;} + if (s1 == s5){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + + // Equal comparisons. Error if they are not equal + if (s1 != s1){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1 != s0){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + + // Substring match. Error if they are not equal + if (s1.strstr (s2) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.strstr (s3) != 2){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s3.strstr (s1) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.strstr (s4) != 1){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + + // Substring creation. Error if they are not equal + if (s1.substring (0) != s1){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.substring (1) != s4){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.substring (2, 2) != s3){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.substring (0, 0) != empty_string){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.substring (4, 10).length () != 1){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + + // Forward search. Error if they are not equal + if (s1.find (s3) != 2){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s3.find (s1) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.find (s3, 2) != 2){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s3.find (s1, 1) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.find (s2) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.find ('o') != 4){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + + // Reverse search. Error if they are not equal + if (s1.rfind ('l') != 3){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + if (s1.rfind ('l', 3) != 2){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + + // Assignment. Error if they are not equal + ACE_CString s6; + s6 = s0; + if (s6 != s0){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + s6 = s4; + if (s4 != s6){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + s6 = s5; + if (s6 != s5){ACE_ERROR((LM_ERROR,"Set #1: \n"));return 1;} + } + + { + /* Set #2 */ + ACE_CString s0 = "hello"; + ACE_CString s1 ("hello", 0, false); + ACE_CString s2 ("world", 0, false); + ACE_CString s3 ("ll", 0, false); + ACE_CString s4 ("ello", 0, false); + ACE_CString s5 = s1 + " " + s2; + + char single_character = 'z'; + ACE_CString single_character_string (single_character); + + ACE_CString empty_string (0, 0, false); + ACE_CString zero_size_string (s1.c_str (), 0, 0, false); + + // Not equal comparisons. Error if they are equal + if (s1 == s2){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1 == s5){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Equal comparisons. Error if they are not equal + if (s1 != s1){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1 != s0){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Substring match. Error if they are not equal + if (s1.strstr (s2) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.strstr (s3) != 2){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s3.strstr (s1) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.strstr (s4) != 1){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Substring creation. Error if they are not equal + if (s1.substring (0) != s1){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.substring (1) != s4){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.substring (2, 2) != s3){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.substring (0, 0) != empty_string){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Forward search. Error if they are not equal + if (s1.find (s3) != 2){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s3.find (s1) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.find (s3, 2) != 2){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s3.find (s1, 1) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.find (s2) != ACE_CString::npos){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.find ('o') != 4){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Reverse search. Error if they are not equal + if (s1.rfind ('l') != 3){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + if (s1.rfind ('l', 3) != 2){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Assignment. Error if they are not equal + ACE_CString s6; + s6 = s0; + if (s6 != s0){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + s6 = s4; + if (s4 != s6){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + s6 = s5; + if (s6 != s5){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Clear. Error if they are not equal + s0.clear(); + if (s0.length() != 0){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + + // Rep. Error if they are not equal + ACE_Auto_Basic_Array_Ptr<char> s (s1.rep ()); + if (ACE_OS::strlen (s.get ()) != s1.length ()) + { + ACE_ERROR((LM_ERROR,"Auto_ptr s: \n")); + }; + + ACE_CString s7 (s.get ()); + if (s1 != s7){ACE_ERROR((LM_ERROR,"Set #2: \n"));return 1;} + } + + { + /* Set #3 */ + ACE_NS_WString s0 ("hello"); + ACE_NS_WString s1 ("hello"); + ACE_NS_WString s2 ("world"); + ACE_NS_WString s3 ("ll"); + ACE_NS_WString s4 ("ello"); + ACE_NS_WString s5 = s1 + " " + s2; + ACE_NS_WString s6 = ("hella"); // Same length as s1, off by one char. + + ACE_WCHAR_T single_character = 'z'; + ACE_NS_WString single_character_string (single_character); + + ACE_NS_WString empty_string; + ACE_NS_WString zero_size_string (s1.c_str (), 0, 0); + + // Not equal comparisons. Error if they are equal + if (s1 == s2){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1 == s5){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1 == s6){ACE_ERROR((LM_ERROR,"Set #3: off-by-one failed\n"));return 1;} + + // Equal comparisons. Error if they are not equal + if (s1 != s1){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1 != s0){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + + // Substring match. Error if they are not equal + if (s1.strstr (s2) != ACE_NS_WString::npos){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.strstr (s3) != 2){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s3.strstr (s1) != ACE_NS_WString::npos){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.strstr (s4) != 1){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + + // Substring creation. Error if they are not equal + if (s1.substring (0) != s1){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.substring (1) != s4){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.substring (2, 2) != s3){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.substring (0, 0) != empty_string){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + + // Forward search. Error if they are not equal + if (s1.find (s3) != 2){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s3.find (s1) != ACE_NS_WString::npos){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.find (s3, 2) != 2){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s3.find (s1, 1) != ACE_NS_WString::npos){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.find (s2) != ACE_NS_WString::npos){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.find ('o') != 4){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + + // Reverse search. Error if they are not equal + if (s1.rfind ('l') != 3){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + if (s1.rfind ('l', 3) != 2){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + + // Assignment. Error if they are not equal + ACE_NS_WString s7; + s7 = s0; + if (s7 != s0){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + s7 = s4; + if (s4 != s7){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + s7 = s5; + if (s7 != s5){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + + // Clear. Error if they are not equal + s0.clear(); + if (s0.length() != 0){ACE_ERROR((LM_ERROR,"Set #3: \n"));return 1;} + } + + { + /* Set #4 */ + ACE_CString s1("dog"); + ACE_CString s2("d"); + + if (s1 == s2){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if (!(s1 > s2)){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if (s1 < s2){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + + ACE_CString s3 ("dog"); + ACE_CString s4 ("dogbert"); + + if (s3 == s4){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if (!(s3 < s4)){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if (s3 > s4){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + + ACE_CString s5 ("dogbert",3); + ACE_CString s6 ("dogbert",5); + + if(s5 == s6){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(!(s5 < s6)){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(s5 > s6){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + + ACE_CString s7 ("dogbert",4); + ACE_CString s8 ("dogbert",2); + + if(s7 == s8){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(!(s7 > s8)){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(s7 < s8){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + + ACE_CString s9 ("dogbert",3); + ACE_CString s10 ("dogbert"); + + if(s9 == s10){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(!(s9 < s10)){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(s9 > s10){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + + ACE_CString s11 ("dogbert",5); + ACE_CString s12 ("dog"); + + if(s11 == s12){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(!(s11 > s12)){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + if(s11 < s12){ACE_ERROR((LM_ERROR,"Set #4: \n"));return 1;} + + s11.fast_clear (); + if (s11.length () != 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("fast_clear didn't yield 0 length\n"))); + } + + { + // Set 1 for ACE_SString, which is not tested + ACE_SString sstr; + + const char *old = sstr.rep (); + const char *str = "What_a_day_it_has_been"; + + sstr.rep (const_cast<char *>(str)); + + ACE_SString tmp = + sstr.substring (2, 300); + + if (tmp.length () == 300) + ACE_ERROR ((LM_ERROR, "SString substring \n")); + + // Constring an ACE_SString without a character pointer or from an + // existing ACE_SString causes memory to be allocated that will not + // be delete (apparently by design). + ACE_Allocator::instance ()->free (const_cast<char *> (old)); + ACE_Allocator::instance ()->free (const_cast<char *> (tmp.rep ())); + } + + int err = testConcatenation(); + + ACE_END_TEST; + return err; +} diff --git a/ACE/tests/STL_algorithm_Test_T.cpp b/ACE/tests/STL_algorithm_Test_T.cpp new file mode 100644 index 00000000000..cb9f63d71b5 --- /dev/null +++ b/ACE/tests/STL_algorithm_Test_T.cpp @@ -0,0 +1,80 @@ +// $Id$ + +#ifndef ACE_TESTS_STL_ALGORITHM_TEST_T_CPP +#define ACE_TESTS_STL_ALGORITHM_TEST_T_CPP + +#include "test_config.h" +#include <algorithm> +#include <typeinfo> + +template <typename T> +class Element_Counter +{ +public: + Element_Counter (void) + : count_ (0) + { + + } + + void operator () (typename T::value_type & item) + { + ++ this->count_; + ACE_UNUSED_ARG (item); + } + + const Element_Counter & operator = (const Element_Counter & ec) + { + this->count_ = ec.count_; + return *this; + } + + typename T::difference_type get_count (void) const + { + return this->count_; + } + +private: + // Number of elements iterated over. + typename T::difference_type count_; +}; + +template <typename T> +int test_STL_algorithm (T & container) +{ + // We are only validating that the container's iterators + // compile with the <algorithm> header. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("running STL algorithm test for `%s'\n"), + typeid (T).name ())); + + // Test the forward iterator using std::for_each. + ACE_DEBUG ((LM_DEBUG, + "testing forward iterator\n")); + + typename T::difference_type count = + std::for_each (container.begin (), + container.end (), + Element_Counter <T> ()).get_count (); + + ACE_DEBUG ((LM_DEBUG, + "std::for_each handled %d elements\n", + count)); + + // Test the reverse iterator using std::for_each. + ACE_DEBUG ((LM_DEBUG, + "testing reverse iterator\n")); + + count = + std::for_each (container.rbegin (), + container.rend (), + Element_Counter <T> ()).get_count (); + + ACE_DEBUG ((LM_DEBUG, + "std::for_each handled %d elements\n", + count)); + + return 0; +} + +#endif /* ACE_TESTS_STL_ALGORITHM_TEST_T_CPP */ diff --git a/ACE/tests/STL_algorithm_Test_T.h b/ACE/tests/STL_algorithm_Test_T.h new file mode 100644 index 00000000000..1b045c12ba5 --- /dev/null +++ b/ACE/tests/STL_algorithm_Test_T.h @@ -0,0 +1,29 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file STL_algorithm_Test_T.h + * + * Test ACE containers compatibility with STL <algorithm> header. + * + * $Id$ + * + * @author James H. Hill <j.hill@vanderbilt.edu> + */ +//============================================================================= + +#ifndef ACE_TESTS_STL_ALGORITHM_TEST_T_H +#define ACE_TESTS_STL_ALGORITHM_TEST_T_H + +template <typename T> +int test_STL_algorithm (T & container); + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "STL_algorithm_Test_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("STL_algorithm_Test_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#endif /* ACE_TESTS_STL_ALGORITHM_TEST_T_H */ diff --git a/ACE/tests/SV_Shared_Memory_Test.cpp b/ACE/tests/SV_Shared_Memory_Test.cpp new file mode 100644 index 00000000000..8fd73b158e6 --- /dev/null +++ b/ACE/tests/SV_Shared_Memory_Test.cpp @@ -0,0 +1,201 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// SV_Shared_Memory_Test.cpp +// +// = DESCRIPTION +// This is a simple test of <ACE_SV_Shared_Memory> and +// <ACE_Malloc> using the <ACE_Shared_Memory_Pool>. The test +// forks two processes and then executes client and server +// allowing them to exchange data using shared memory. No user +// input is required as far as command line arguments are +// concerned. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// and Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Malloc_T.h" +#include "ace/Shared_Memory_Pool.h" +#include "ace/SV_Semaphore_Simple.h" +#include "ace/SV_Semaphore_Complex.h" +#include "ace/OS_NS_unistd.h" + + +ACE_RCSID(tests, SV_Shared_Memory_Test, "$Id$") + +#if defined (ACE_HAS_SYSV_IPC) && !defined(ACE_LACKS_SYSV_SHMEM) + +// The shared memory allocator, which uses up the ACE_DEFAULT_SEM_KEY. +// We hide the allocator inside this function so that it doesn't get +// constructed until after the ACE_Object_Manager gets constructed, +// even with ACE_HAS_NONSTATIC_OBJECT_MANAGER. + +static +ACE_Malloc<ACE_SHARED_MEMORY_POOL, ACE_SV_Semaphore_Simple> & +myallocator (void) +{ + static ACE_Malloc<ACE_SHARED_MEMORY_POOL, + ACE_SV_Semaphore_Simple> myallocator; + return myallocator; +} + +// Create some more keys that are different from the +// ACE_DEFAULT_SEM_KEY used by the allocator. +static const int SEM_KEY_1 = ACE_DEFAULT_SEM_KEY + 1; +static const int SEM_KEY_2 = ACE_DEFAULT_SEM_KEY + 2; + +static const int SHMSZ = 27; +static const char SHMDATA[SHMSZ] = "abcdefghijklmnopqrstuvwxyz"; + +static ACE_SV_Semaphore_Complex *parent_mutex = 0; +static ACE_SV_Semaphore_Complex *parent_synch = 0; + +static int +parent (char *shm) +{ + // This for loop executes in a critical section proteced by + // <parent_mutex>. + for (int i = 0; i < SHMSZ; i++) + shm[i] = SHMDATA[i]; + + int result; + result = parent_mutex->release (); + ACE_ASSERT (result != -1); + + result = parent_synch->acquire (); + ACE_ASSERT (result != -1); + + result = myallocator ().remove (); + ACE_ASSERT (result != -1); + + result = parent_mutex->remove (); + ACE_ASSERT (result != -1); + + result = parent_synch->remove (); + ACE_ASSERT (result != -1); + + return 0; +} + +static int +child (char *shm) +{ + int result; + + ACE_SV_Semaphore_Complex mutex; + + // This semaphore is initially created with a count of 0, i.e., it + // is "locked." + result = mutex.open (SEM_KEY_1, + ACE_SV_Semaphore_Complex::ACE_CREATE, + 0); + ACE_ASSERT (result != -1); + + ACE_SV_Semaphore_Complex synch; + // This semaphore is initially created with a count of 0, i.e., it + // is "locked." + result = synch.open (SEM_KEY_2, + ACE_SV_Semaphore_Complex::ACE_CREATE, + 0); + ACE_ASSERT (result != -1); + + // Perform "busy waiting" here until we acquire the semaphore. This + // isn't really a good design -- it's just to illustrate that you + // can do non-blocking acquire() calls with the ACE System V + // semaphore wrappers. + while ((result = mutex.tryacquire ()) == -1) + if (errno == EAGAIN) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P) spinning in child!\n"))); + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P) child mutex.tryacquire"))); + ACE_ASSERT (result != -1); + } + + for (int i = 0; i < SHMSZ; i++) + ACE_ASSERT (SHMDATA[i] == shm[i]); + + result = synch.release (); + ACE_ASSERT (result != -1); + + return 0; +} + +#endif /* ACE_HAS_SYSV_IPC */ +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("SV_Shared_Memory_Test")); + +#if defined (ACE_HAS_SYSV_IPC) && !defined (ACE_LACKS_FORK) && \ + !defined(ACE_LACKS_SYSV_SHMEM) + + // Check whether allocator was initialized. + if (myallocator ().bad ()) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to initialize allocator\n")), + -1); + } + + char *shm = reinterpret_cast<char *> (myallocator ().malloc (SHMSZ)); + + // Create the mutex and synch before spawning the child process, to + // avoid race condition between their creation in the parent and use + // in the child. + ACE_NEW_RETURN (parent_mutex, + ACE_SV_Semaphore_Complex, + -1); + ACE_NEW_RETURN (parent_synch, + ACE_SV_Semaphore_Complex, + -1); + + // This semaphore is initially created with a count of 0, i.e., it + // is "locked." + int result = parent_mutex->open (SEM_KEY_1, + ACE_SV_Semaphore_Complex::ACE_CREATE, + 0); + ACE_ASSERT (result != -1); + + // This semaphore is initially created with a count of 0, i.e., it + // is "locked." + result = parent_synch->open (SEM_KEY_2, + ACE_SV_Semaphore_Complex::ACE_CREATE, + 0); + ACE_ASSERT (result != -1); + + switch (ACE_OS::fork (ACE_TEXT ("SV_Shared_Memory_Test.cpp"))) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P) fork failed\n")), + -1); + /* NOTREACHED */ + case 0: + child (shm); + break; + default: + parent (shm); + delete parent_mutex; + delete parent_synch; + break; + } +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("SYSV IPC, SYSV SHMEM, or fork ") + ACE_TEXT ("are not supported on this platform\n"))); +#endif /* ACE_HAS_SYSV_IPC */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Semaphore_Test.cpp b/ACE/tests/Semaphore_Test.cpp new file mode 100644 index 00000000000..75fe98f848e --- /dev/null +++ b/ACE/tests/Semaphore_Test.cpp @@ -0,0 +1,252 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Semaphore Test +// +// = DESCRIPTION +// This test verifies the functionality of the <ACE_Thread_Semaphore> +// implementation. +// +// = AUTHOR +// Darrell Brunsch <brunsch@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread_Semaphore.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Semaphore_Test, "$Id$") + +// msec that times are allowed to differ before test fails. +#if defined (ACE_HAS_HI_RES_TIMER) || defined (ACE_HAS_AIX_HI_RES_TIMER) || \ + defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER) || \ + defined (ACE_HAS_POWERPC_TIMER) +# define ACE_ALLOWED_SLACK 100 +#else /* don't have a high-res timer */ +# define ACE_ALLOWED_SLACK 1100 +#endif /* don't have a high-res timer */ + +// Test results, 'success' is 0 +static int test_result = 0; + +#if defined (ACE_HAS_THREADS) + +// Semaphore used in the tests. Start it "locked" (i.e., its initial +// count is 0). +static ACE_Thread_Semaphore s ((unsigned int) 0); + +// Default number of iterations. +static int n_iterations = 10; + +// Number of worker threads. +static size_t n_workers = 10; + +// Amount to release the semaphore. +static u_int n_release_count = 3; + +// Number of timeouts. +static size_t timeouts = 0; + +// Number of times to call test_timeout (). +static size_t test_timeout_count = 3; + +// Tests the amount of time spent in a timed wait. +static int +test_timeout (void) +{ + int status = 0; + + // milliseconds... + long msecs_expected; + long msecs_waited; + long msecs_diff; + + // Wait a little longer each time + static long wait_secs = 3; + + ACE_Time_Value wait = ACE_OS::gettimeofday (); + + ACE_Time_Value begin = wait; + + wait.sec (wait.sec () + wait_secs); + + if (s.acquire (wait) == -1) + { + if (errno != ETIME) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("test_timeout should be ETIME but is"))); + status = -1; + } + } + + ACE_Time_Value wait_diff = ACE_OS::gettimeofday () - begin; + + msecs_waited = wait_diff.msec (); + msecs_expected = wait_secs * 1000; + msecs_diff = labs (msecs_expected - msecs_waited); + + if (msecs_diff > ACE_ALLOWED_SLACK) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Timed wait fails length test\n"))); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Expected %d ms, actual %d ms; %d allowed\n"), + (int)msecs_expected, + (int)msecs_waited, + (int)ACE_ALLOWED_SLACK)); + status = -1; + } + + ++wait_secs; + return status; +} + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-s n_release_count] [-w n_workers] [-n iteration_count]\n"))); + ACE_OS::exit (1); +} + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("s:w:n:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 's': + n_release_count = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'w': + n_workers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + n_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + print_usage_and_die (); + break; + } +} + +// Worker tries to acquire the semaphore, hold it for a while, and +// then releases it. + +static void * +worker (void *) +{ + for (int iterations = 1; + iterations <= n_iterations; + iterations++) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_Time_Value wait (0, + iterations * 1000 * 100); // Wait 'iter' msec + //FUZZ: enable check_for_lack_ACE_OS + + ACE_Time_Value tv = ACE_OS::gettimeofday () + wait; + if (s.acquire (tv) == -1) + { + // verify that we have ETIME + if (ACE_OS::last_error() != ETIME) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Worker should be ETIME but is"))); + } + else + ++timeouts; + ACE_Time_Value diff = ACE_OS::gettimeofday (); + diff = diff - tv; // tv should have been reset to time acquired + long diff_msec = diff.msec (); + + if (diff_msec > ACE_ALLOWED_SLACK) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Acquire fails time reset test\n"))); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Diff btw now and returned time: %d ms; ") + ACE_TEXT ("%d allowed\n"), + (int)diff_msec, + (int)ACE_ALLOWED_SLACK)); + test_result = 1; + } + // Hold the lock for a while. + ACE_OS::sleep (ACE_Time_Value (0, + (ACE_OS::rand () % 1000) * 1000)); + s.release (); + } + ACE_Thread::yield (); + } + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Test semaphore functionality. + +int run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Semaphore_Test")); + +#if defined (ACE_HAS_THREADS) + parse_args (argc, argv); + ACE_OS::srand ((u_int) ACE_OS::time (0L)); + + //Test timed waits. + for (size_t i = 0; i < test_timeout_count; i++) + if (test_timeout () != 0) + test_result = 1; + + // Release the semaphore a certain number of times. + s.release (n_release_count); + + if (ACE_Thread_Manager::instance ()->spawn_n + (static_cast<size_t> (n_workers), + ACE_THR_FUNC (worker), + 0, + THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn_n")), + 1); + + ACE_Thread_Manager::instance ()->wait (); + + size_t percent = (timeouts * 100) / (n_workers * n_iterations); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Worker threads timed out %d percent of the time\n"), + (int)percent)); + + if (test_result == 0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Semaphore Test successful\n"))); +#else + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return test_result; +} diff --git a/ACE/tests/Sendfile_Test.cpp b/ACE/tests/Sendfile_Test.cpp new file mode 100644 index 00000000000..144b42657b8 --- /dev/null +++ b/ACE/tests/Sendfile_Test.cpp @@ -0,0 +1,337 @@ +// $Id$ + +/** + * @file Sendfile_Test.cpp + * + * This is a test of the @c ACE_OS::sendfile() wrapper function. It + * is primarily meant to test the case when ACE_HAS_SENDFILE is not + * defined, i.e. when sendfile() support is emulated. + * + * @author + * Steve Huston <shuston@riverace.com> + * Ossama Othman <ossama@dre.vanderbilt.edu> + */ + +#include "test_config.h" +#include "ace/OS_NS_sys_wait.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Time_Value.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/OS_NS_sys_sendfile.h" + +// Change to non-zero if test fails +static int Test_Result = 0; + +#if !defined (ACE_LACKS_FORK) || defined (ACE_HAS_THREADS) + +// This test sends a large amount of data. The purpose is to overflow the +// TCP send window, causing the sender to block (it's a send_n). This value +// is the amount to send. The assumption is that no implementation has a +// receive window larger than 128K bytes. If one is found, this is the place +// to change it. +// For some odd reason, NT will try to send a single large buffer, but not +// multiple smaller ones that add up to the large size. +const size_t Test3_Send_Size = 4*1024; +const size_t Test3_Loops = 10; +const size_t Test3_Total_Size = Test3_Send_Size * Test3_Loops; + + +static void * +client (void *arg) +{ + ACE_INET_Addr *remote_addr = reinterpret_cast<ACE_INET_Addr *> (arg); + ACE_INET_Addr server_addr (remote_addr->get_port_number (), + ACE_LOCALHOST); + ACE_SOCK_Stream cli_stream; + ACE_SOCK_Connector con; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Connecting to port %d\n"), + server_addr.get_port_number())); + + // Initiate connection with server; don't wait forever + if (con.connect (cli_stream, + server_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed"))); + Test_Result = 1; + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) connected to %s\n"), + ACE_TEXT_CHAR_TO_TCHAR(server_addr.get_host_name ()))); + + //******************* TEST 1 ****************************** + // + // Send the 255 byte buffer in 5 chunks. The + // server will verify that the correct data is sent, and that there + // is no more and no less. + + u_char buffer[255]; + size_t i; + ssize_t byte_count = 0; + ssize_t len = 0; + off_t offset = 0; + + // The server will verify that this data pattern gets there intact. + + for (i = 0; i < sizeof buffer; ++i) + buffer[i] = static_cast<u_char> (i); + + ACE_TCHAR const test_file[] = ACE_TEXT ("Sendfile_Test_File"); + ACE_HANDLE in_fd = + ACE_OS::open (test_file, + O_CREAT | O_RDWR | O_TRUNC, + ACE_DEFAULT_FILE_PERMS); + + if (in_fd == ACE_INVALID_HANDLE) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) open %p\n"), test_file)); + Test_Result = 1; + goto cleanup; + } + + ACE_OS::unlink (test_file); + + byte_count = ACE_OS::write (in_fd, buffer, sizeof (buffer)); + + if (byte_count != static_cast<ssize_t> (sizeof (buffer))) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) write %p\n"), test_file)); + Test_Result = 1; + } + + len = ACE_OS::sendfile (cli_stream.get_handle (), + in_fd, + &offset, + byte_count); + + if (len == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Test 1, sendfile failed"))); + Test_Result = 1; + goto cleanup; + } + else if (len != 255) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) sendfile len %b; should be 255\n"), + len)); + + //******************* TEST 2 ****************************** + // + // The same data is coming back - receive it using recv (size_t n, + // ...) and compare it to the original data. + + u_char buffer2[255]; + // Give it a chance to get here + ACE_OS::sleep (2); + len = cli_stream.recv (4, + buffer2, + 150, + &buffer2[150], + 105); + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv len is %b, but should be 255!\n"), + len)); + } + + for (i = 0; i < static_cast<size_t>(len); i++) + if (buffer2[i] != buffer[i]) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 2, rcvd byte %B is %d, not %d\n"), + i, buffer2[i], buffer[i])); + Test_Result = 1; + } + +cleanup: + cli_stream.close (); + (void) ACE_OS::close (in_fd); + + return 0; +} + +static void * +server (void *arg) +{ + ACE_SOCK_Acceptor *peer_acceptor = (ACE_SOCK_Acceptor *) arg; + ACE_SOCK_Stream sock_str; + ACE_INET_Addr cli_addr; + ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT); + + // Accept the connection over which the stream tests will run. + // Don't lock up if client doesn't connect + if (peer_acceptor->accept (sock_str, + &cli_addr, + &timeout) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("accept"))); + Test_Result = 1; + return 0; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client %s connected from %d\n"), + ACE_TEXT_CHAR_TO_TCHAR(cli_addr.get_host_name ()), + cli_addr.get_port_number ())); + + //******************* TEST 1 ****************************** + // + // Do a iovec recvv - the client should send 255 bytes, which we + // will be detected and read into a ACE-allocated buffer. Use a 5 + // second timeout to give the client a chance to send it all. + + ACE_OS::sleep (5); + + u_char buffer[255]; + ssize_t len; + int i; + + len = sock_str.recv (buffer, 255); + if (len == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("Test 1, recv failed"))); + Test_Result = 1; + } + + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) recv len is %b, but should be 255!\n"), + len)); + } + + for (i = 0; i < static_cast<int>(len); i++) + if (buffer[i] != i) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Test 1, rcvd byte %d is %d, not %d\n"), + i, + buffer[i], + i)); + Test_Result = 1; + } + + //******************* TEST 2 ****************************** + // + // Send the buffer back, using send (size_t n, ...) in 3 pieces. + + len = sock_str.send (6, + buffer, + 42, + &buffer[42], + 189, + &buffer[231], + 24); + if (len != 255) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) send len is %b, but should be 255!\n"), + len)); + } + + sock_str.close(); + + return 0; +} + +#endif /* !ACE_LACKS_FORK || ACE_HAS_THREADS */ + +static void +spawn (void) +{ + // Acceptor + ACE_SOCK_Acceptor peer_acceptor; + + // Create a server address. + ACE_INET_Addr server_addr; + + // Bind listener to any port and then find out what the port was. + if (peer_acceptor.open (ACE_Addr::sap_any) == -1 + || peer_acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("open"))); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + server_addr.get_port_number ())); + +#if !defined (ACE_LACKS_FORK) + switch (ACE_OS::fork (ACE_TEXT ("child"))) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("fork failed"), + 1)); + /* NOTREACHED */ + case 0: + client (&server_addr); + ACE_OS::exit (0); + /* NOTREACHED */ + default: + server (reinterpret_cast<void *> (&peer_acceptor)); + ACE_OS::wait (); + } +#elif defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (server), + reinterpret_cast<void *> (&peer_acceptor), + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (client), + reinterpret_cast<void *> (&server_addr), + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("thread create failed"), + 1)); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("(%P|%t) ") + ACE_TEXT ("only one thread may be run ") + ACE_TEXT ("in a process on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + peer_acceptor.close (); + } +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Sendfile_Test")); + + spawn (); + + ACE_END_TEST; + return Test_Result; +} diff --git a/ACE/tests/Service_Config_DLL.cpp b/ACE/tests/Service_Config_DLL.cpp new file mode 100644 index 00000000000..c3d4be87e69 --- /dev/null +++ b/ACE/tests/Service_Config_DLL.cpp @@ -0,0 +1,274 @@ +// -*- C++ -*- +//============================================================================= +/** + * @file Service_Config_DLL.cpp + * + * $Id$ + * + * This file is related to, and used with, Service_Config_Test. It's + * used when testing the reentrance/thread-safety of the + * Service Configurator, in addition to testing the Service + * Configurator's ability to handle nested processing of Service + * Configurator directives. + * + * @author Ossama Othman <ossama@uci.edu> + */ +//============================================================================= + +#include "Service_Config_DLL.h" +#include "ace/Service_Config.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" + +ACE_RCSID (tests, + Service_Config_DLL, + "$Id$") + +static ACE_THR_FUNC_RETURN +invoke_service_config (void *arg) +{ + const ACE_TCHAR *directive = reinterpret_cast<const ACE_TCHAR *> (arg); + + + // Process a Service Configurator directive in the current thread. + if (ACE_Service_Config::process_directive (directive) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Service_Config_DLL::svc() - ") + ACE_TEXT ("process_directive() failed for:\n") + ACE_TEXT ("\"%s\"\n"), + directive)); + + return 0; +} + +Service_Config_DLL::Service_Config_DLL (void) +{ + ACE_OS::memset (this->directive_[0], 0, BUFSIZ * sizeof (ACE_TCHAR)); + ACE_OS::memset (this->directive_[1], 0, BUFSIZ * sizeof (ACE_TCHAR)); +} + +int +Service_Config_DLL::init (int argc, ACE_TCHAR *argv[]) +{ + if (argc == 2) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Loading Test_Object_%s and Test_Object_%s\n"), + argv[0], + argv[1])); + + ACE_OS::sprintf (this->directive_[0], +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + ACE_TEXT ("dynamic Test_Object_%s Service_Object * Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_%s\""), +#else + ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Test_Object_%s' type='service_object'> <initializer init='_make_Service_Config_DLL' path='Service_Config_DLL' params='Test_Object_%s'/> </dynamic>"), +#endif + argv[0], + argv[0]); + + ACE_OS::sprintf (this->directive_[1], +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + ACE_TEXT ("dynamic Test_Object_%s Service_Object * Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_%s\""), +#else + ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Test_Object_%s' type='service_object'> <initializer init='_make_Service_Config_DLL' path='Service_Config_DLL' params='Test_Object_%s'/> </dynamic>"), +#endif + + argv[1], + argv[1]); + + if (ACE_Service_Config::process_directive (this->directive_[0]) != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Service_Config_DLL::init() - ") + ACE_TEXT ("process_directive() failed for:\n") + ACE_TEXT ("\"%s\": %m\n"), + this->directive_[0])); + +#if defined (ACE_HAS_THREADS) + + // Become an Active Object if more than one argument passed. + // Two arguments indicate two "test objects" to be dynamically + // loaded. + return this->activate (); + +#endif /* ACE_HAS_THREADS */ + + } + else if (argc == 1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Service_Config_DLL::init () %@ - %s\n"), + this, + argv[0])); + } + else + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Incorrect number of arguments ") + ACE_TEXT ("(%d) passed to Service_Config_DLL::init ()"), + argc), + -1); + } + + return 0; +} + +int +Service_Config_DLL::fini (void) +{ + return 0; +} + +int +Service_Config_DLL::svc (void) +{ + if (ACE_Thread_Manager::instance ()->spawn (invoke_service_config, + this->directive_[1]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Unable to spawn thread to ") + ACE_TEXT ("invoke Service Configurator.\n")), + -1); + + return 0; +} + +// The same class (Service_Config_DLL) is used to implement each of the +// Service Objects whose service descriptors are defined below. + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_1, + ACE_TEXT ("Test_Object_1"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_2, + ACE_TEXT ("Test_Object_2"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_3, + ACE_TEXT ("Test_Object_3"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_4, + ACE_TEXT ("Test_Object_4"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_5, + ACE_TEXT ("Test_Object_5"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_6, + ACE_TEXT ("Test_Object_6"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Final_Object, + ACE_TEXT ("Final_Object"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_1_More, + ACE_TEXT ("Test_Object_1_More"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + +// ----------------------------------------------------------------- + +ACE_STATIC_SVC_DEFINE (Test_Object_2_More, + ACE_TEXT ("Test_Object_2_More"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (Service_Config_DLL), + ACE_Service_Type::DELETE_THIS + | ACE_Service_Type::DELETE_OBJ, + 0) + + +// ----------------------------------------------------------------- + +// Same factory is used for all service descriptors defined above. +ACE_FACTORY_DEFINE (Service_Config_DLL, Service_Config_DLL) + +/* +** Simple service which will refuse to load/initialize correctly. +** The main program should do: +** 1. Try to load this service (which should fail) +** 2. Try to look up this service and call its info() hook; if info() +** can be called, the test has failed. +** Similarly, if fini() is called later, something is very wrong. +*/ + +/// Initializes object when dynamic linking occurs. +int +Refuses_Init::init (int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Refuses_Init::init - refusing to init\n"))); + return -1; +} + +/// Terminates object when dynamic unlinking occurs. +int +Refuses_Init::fini (void) +{ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Refuses_Init::fini should not be called!\n"))); + return 0; +} + +/// Returns information on a service object. +int +Refuses_Init::info (ACE_TCHAR **info_string, size_t length) const +{ + ACE_TCHAR const *msg = + ACE_TEXT ("Refuses_Init service, shouldn't be here!\n"); + if (*info_string == 0) + *info_string = ACE_OS::strdup (msg); + else + ACE_OS::strncpy (*info_string, msg, length); + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Refuses_Init::info() called - shouldn't be\n"))); + return ACE_OS::strlen (*info_string); +} + +ACE_FACTORY_DEFINE (Service_Config_DLL, Refuses_Init) diff --git a/ACE/tests/Service_Config_DLL.h b/ACE/tests/Service_Config_DLL.h new file mode 100644 index 00000000000..7eebe0e6364 --- /dev/null +++ b/ACE/tests/Service_Config_DLL.h @@ -0,0 +1,91 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Service_Config_DLL.h + * + * $Id$ + * + * @author Ossama Othman <ossama@uci.edu> + */ +//============================================================================= + +#ifndef SERVICE_CONFIG_DLL_H +#define SERVICE_CONFIG_DLL_H + +#include /**/ "ace/pre.h" + +#include "Service_Config_DLL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Service_Object.h" +#include "ace/Task.h" + +/** + * @class Service_Config_DLL + * + * @brief The Service_Config_DLL that is instantiated when the + * client-side test module/library is dynamically loaded. + * + * This class is the implementation used for all service instances + * (i.e. those declared using the ACE_FACTORY_* macros). + */ +class Service_Config_DLL_Export Service_Config_DLL : public ACE_Task_Base +{ +public: + + /// Constructor. + Service_Config_DLL (void); + + /// Initializes object when dynamic linking occurs. + virtual int init (int argc, ACE_TCHAR *argv[]); + + /// Terminates object when dynamic unlinking occurs. + virtual int fini (void); + + /// Run by a daemon thread. + /** + * Each thread will invoke the Service Configurator via this + * method unless the object is the "FINAL" object. + */ + virtual int svc (void); + +private: + + /// Directives to be passed to be processed by the Service + /// Configurator in seperate threads. + ACE_TCHAR directive_[2][BUFSIZ]; + +}; + +/** + * @class Refuses_Init + * + * @brief The Refuses_Init service simply tests for proper operation + * when a service's init() hook returns -1. + */ +class Service_Config_DLL_Export Refuses_Init : public ACE_Service_Object +{ +public: + // Constructor; required for factory function. + Refuses_Init () {} + + /// Initializes object when dynamic linking occurs. + virtual int init (int argc, ACE_TCHAR *argv[]); + + /// Terminates object when dynamic unlinking occurs. + virtual int fini (void); + + /// Returns information on a service object. + virtual int info (ACE_TCHAR **info_string, size_t length = 0) const; +}; + +ACE_FACTORY_DECLARE (Service_Config_DLL, Service_Config_DLL) +ACE_FACTORY_DECLARE (Service_Config_DLL, Refuses_Init) + +#include /**/ "ace/post.h" + +#endif /* SERVICE_CONFIG_DLL_H */ diff --git a/ACE/tests/Service_Config_DLL_Export.h b/ACE/tests/Service_Config_DLL_Export.h new file mode 100644 index 00000000000..1688a745350 --- /dev/null +++ b/ACE/tests/Service_Config_DLL_Export.h @@ -0,0 +1,38 @@ + +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. +// This file is generated automatically by generate_export_file.pl +// ------------------------------ +#ifndef SERVICE_CONFIG_DLL_EXPORT_H +#define SERVICE_CONFIG_DLL_EXPORT_H + +#include "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (SERVICE_CONFIG_DLL_HAS_DLL) +# define SERVICE_CONFIG_DLL_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && ! SERVICE_CONFIG_DLL_HAS_DLL */ + +#if !defined (SERVICE_CONFIG_DLL_HAS_DLL) +# define SERVICE_CONFIG_DLL_HAS_DLL 1 +#endif /* ! TEST_HAS_DLL */ + +#if defined (SERVICE_CONFIG_DLL_HAS_DLL) && (SERVICE_CONFIG_DLL_HAS_DLL == 1) +# if defined (SERVICE_CONFIG_DLL_BUILD_DLL) +# define Service_Config_DLL_Export ACE_Proper_Export_Flag +# define TEST_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# define TEST_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_EXPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# else /* SERVICE_CONFIG_DLL_BUILD_DLL */ +# define Service_Config_DLL_Export ACE_Proper_Import_Flag +# define TEST_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# define TEST_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_IMPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# endif /* SERVICE_CONFIG_DLL_BUILD_DLL */ +#else /* SERVICE_CONFIG_DLL_HAS_DLL == 1 */ +# define Service_Config_DLL_Export +# define TEST_SINGLETON_DECLARATION(T) +# define TEST_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +#endif /* SERVICE_CONFIG_DLL_HAS_DLL == 1 */ + +#endif /* SERVICE_CONFIG_DLL_EXPORT_H */ + +// End of auto generated file. diff --git a/ACE/tests/Service_Config_Test.UTF-16.conf b/ACE/tests/Service_Config_Test.UTF-16.conf Binary files differnew file mode 100644 index 00000000000..fee216c95f9 --- /dev/null +++ b/ACE/tests/Service_Config_Test.UTF-16.conf diff --git a/ACE/tests/Service_Config_Test.UTF-16.conf.xml b/ACE/tests/Service_Config_Test.UTF-16.conf.xml Binary files differnew file mode 100644 index 00000000000..1d75d2763c0 --- /dev/null +++ b/ACE/tests/Service_Config_Test.UTF-16.conf.xml diff --git a/ACE/tests/Service_Config_Test.WCHAR_T.conf b/ACE/tests/Service_Config_Test.WCHAR_T.conf Binary files differnew file mode 100644 index 00000000000..81f6e1fe4d6 --- /dev/null +++ b/ACE/tests/Service_Config_Test.WCHAR_T.conf diff --git a/ACE/tests/Service_Config_Test.WCHAR_T.conf.xml b/ACE/tests/Service_Config_Test.WCHAR_T.conf.xml Binary files differnew file mode 100644 index 00000000000..88e7c26e8eb --- /dev/null +++ b/ACE/tests/Service_Config_Test.WCHAR_T.conf.xml diff --git a/ACE/tests/Service_Config_Test.conf b/ACE/tests/Service_Config_Test.conf new file mode 100644 index 00000000000..34d51068365 --- /dev/null +++ b/ACE/tests/Service_Config_Test.conf @@ -0,0 +1,19 @@ +# Dynamically loading each of the Service Objects below causes a +# number of threads to be spawned, each one invoking the Service +# Configurator (e.g. ACE_Service_Config::process_directive(). If the +# Service Configurator is thread safe and reentrant, then parsing of +# this `Service_Config_Test.conf' file should run to completion +# without error. +# +# Test_Object_1 will cause Test_Object_2 and Test_Object_3 to be +# dynamically loaded. Dynamic loading of each of object will occur in +# a separate thread. +dynamic Test_Object_1 Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "2 3" + +# Test_Object_4 will cause Test_Object_5 and Test_Object_6 to be +# dynamically loaded. Dynamic loading of each of object will occur in +# a separate thread. +dynamic Test_Object_4 Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "5 6" + +# Final_Object does nothing but print a completion message. +dynamic Final_Object Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "FINAL" diff --git a/ACE/tests/Service_Config_Test.conf.xml b/ACE/tests/Service_Config_Test.conf.xml new file mode 100644 index 00000000000..f3273f0cb93 --- /dev/null +++ b/ACE/tests/Service_Config_Test.conf.xml @@ -0,0 +1,27 @@ +<?xml version='1.0'?> +<!-- Converted from Service_Config_Test.conf by svcconf-convert.pl --> +<ACE_Svc_Conf> + <!-- Dynamically loading each of the Service Objects below causes a --> + <!-- number of threads to be spawned, each one invoking the Service --> + <!-- Configurator (e.g. ACE_Service_Config::process_directive(). If the --> + <!-- Service Configurator is thread safe and reentrant, then parsing of --> + <!-- this `Service_Config_Test.conf' file should run to completion --> + <!-- without error. --> + <!-- --> + <!-- Test_Object_1 will cause Test_Object_2 and Test_Object_3 to be --> + <!-- dynamically loaded. Dynamic loading of each of object will occur in --> + <!-- a separate thread. --> + <dynamic id="Test_Object_1" type="Service_Object"> + <initializer init="_make_Service_Config_DLL" path="Service_Config_DLL" params="2 3"/> + </dynamic> + <!-- Test_Object_4 will cause Test_Object_5 and Test_Object_6 to be --> + <!-- dynamically loaded. Dynamic loading of each of object will occur in --> + <!-- a separate thread. --> + <dynamic id="Test_Object_4" type="Service_Object"> + <initializer init="_make_Service_Config_DLL" path="Service_Config_DLL" params="5 6"/> + </dynamic> + <!-- Final_Object does nothing but print a completion message. --> + <dynamic id="Final_Object" type="Service_Object"> + <initializer init="_make_Service_Config_DLL" path="Service_Config_DLL" params="FINAL"/> + </dynamic> +</ACE_Svc_Conf> diff --git a/ACE/tests/Service_Config_Test.cpp b/ACE/tests/Service_Config_Test.cpp new file mode 100644 index 00000000000..bbecdf9a41e --- /dev/null +++ b/ACE/tests/Service_Config_Test.cpp @@ -0,0 +1,366 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Service_Config_Test.cpp + * + * $Id$ + * + * This is a simple test to make sure the ACE Service Configurator + * framework is working correctly. + * + * @author David Levine <levine@cs.wustl.edu> + * @author Ossama Othman <ossama@uci.edu> + */ +//============================================================================= + +#include "test_config.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_errno.h" +#include "ace/Log_Msg.h" +#include "ace/Object_Manager.h" +#include "ace/Service_Config.h" +#include "ace/Service_Object.h" +#include "ace/Service_Repository.h" +#include "ace/Service_Types.h" +#include "ace/Reactor.h" +#include "ace/Thread_Manager.h" +#include "ace/ARGV.h" + +ACE_RCSID (tests, + Service_Config_Test, + "$Id$") + +static const u_int VARIETIES = 3; + +static u_int error = 0; + +/** + * @class Test_Singleton + * + * @brief Test the Singleton + * + * This should be a template class, with singleton instantiations. + * But to avoid having to deal with compilers that want template + * declarations in separate files, it's just a plain class. The + * instance argument differentiates the "singleton" instances. It + * also demonstrates the use of the param arg to the cleanup () + * function. + */ +class Test_Singleton +{ +public: + static Test_Singleton *instance (u_short variety); + ~Test_Singleton (void); + +private: + u_short variety_; + static u_short current_; + + Test_Singleton (u_short variety); + + friend class misspelled_verbase_friend_declaration_to_avoid_compiler_warning_with_private_ctor; +}; + +u_short Test_Singleton::current_ = 0; + +extern "C" void +test_singleton_cleanup (void *object, void *param) +{ + // We can't reliably use ACE_Log_Msg in a cleanup hook. Yet. + ACE_UNUSED_ARG (param); + /* ACE_DEBUG ((LM_DEBUG, "cleanup %d\n", (u_short) param)); */ + + delete (Test_Singleton *) object; +} + +Test_Singleton * +Test_Singleton::instance (u_short variety) +{ + static Test_Singleton *instances[VARIETIES] = { 0 }; + + if (instances[variety] == 0) + ACE_NEW_RETURN (instances[variety], + Test_Singleton (variety), + 0); + + ACE_Object_Manager::at_exit (instances[variety], + test_singleton_cleanup, + reinterpret_cast<void *> (static_cast<size_t> (variety))); + return instances[variety]; +} + +Test_Singleton::Test_Singleton (u_short variety) + : variety_ (variety) +{ + if (variety_ != current_++) + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("ERROR: instance %u created out of order!\n"), + variety_)); + ++error; + } +} + +// We can't reliably use ACE_Log_Msg in a destructor that is called by +// ACE_Object_Manager. Yet. + +Test_Singleton::~Test_Singleton (void) +{ + /* ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test_Singleton %u dtor\n"), variety_)); */ + + if (variety_ != --current_) + { + ACE_OS::fprintf (stderr, + ACE_TEXT ("ERROR: instance %u destroyed out of order!\n"), + variety_); + /* ACE_DEBUG ((LM_ERROR, ACE_TEXT ("ERROR: instance %u destroyed out of order!\n"), + variety_)); */ + ++error; + } +} + +void +testFailedServiceInit (int, ACE_TCHAR *[]) +{ + static const ACE_TCHAR *refuse_svc = +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + ACE_TEXT ("dynamic Refuses_Svc Service_Object * ") + ACE_TEXT (" Service_Config_DLL:_make_Refuses_Init() \"\"") +#else + ACE_TEXT ("<dynamic id=\"Refuses_Svc\" type=\"Service_Object\">") + ACE_TEXT (" <initializer init=\"_make_Refuses_Init\" path=\"Service_Config_DLL\" params=\"\"/>") + ACE_TEXT ("</dynamic>") +#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ + ; + + int error_count = 0; + if ((error_count = ACE_Service_Config::process_directive (refuse_svc)) != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Failed init test should have returned 1; ") + ACE_TEXT ("returned %d instead\n"), + error_count)); + } + + // Try to find the service; it should not be there. + ACE_Service_Type const *svcp; + if (-1 != ACE_Service_Repository::instance ()->find (ACE_TEXT ("Refuses_Svc"), + &svcp)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Found service repo entry for Refuses_Svc\n"))); + ACE_Service_Type_Impl const *svc_impl = svcp->type (); + ACE_TCHAR msg[1024]; + ACE_TCHAR *msgp = msg; + if (svc_impl->info (&msgp, sizeof (msg) / sizeof (ACE_TCHAR)) > 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Refuses_Svc said: %s\n"), msg)); + } + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Repo reports no Refuses_Svc; correct.\n"))); +} + + +void +testLoadingServiceConfFileAndProcessNo (int argc, ACE_TCHAR *argv[]) +{ + ACE_ARGV new_argv; + +#if defined (ACE_USES_WCHAR) + // When using full Unicode support, use the version of the Service + // Configurator file appropriate to the platform. + // For example, Windows Unicode uses UTF-16. + // + // iconv(1) found on Linux and Solaris, for example, can + // be used to convert between encodings. + // + // Byte ordering is also an issue, so we should be + // generating this file on-the-fly from the UTF-8 encoded + // file by using functions like iconv(1) or iconv(3). +# if defined (ACE_WIN32) + const ACE_TCHAR svc_conf[] = + ACE_TEXT ("Service_Config_Test.UTF-16") + ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); +# else + const ACE_TCHAR svc_conf[] = + ACE_TEXT ("Service_Config_Test.WCHAR_T") + ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); +# endif /* ACE_WIN32 */ +#else + // ASCII (UTF-8) encoded Service Configurator file. + const ACE_TCHAR svc_conf[] = + ACE_TEXT ("Service_Config_Test") + ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); +#endif /* ACE_USES_WCHAR */ + + // Process the Service Configurator directives in this test's Making + // sure we have more than one option with an argument, to capture + // any errors caused by "reshuffling" of the options. + ACE_ASSERT (new_argv.add (argv) != -1 + && new_argv.add (ACE_TEXT ("-d")) != -1 + && new_argv.add (ACE_TEXT ("-k")) != -1 + && new_argv.add (ACE_TEXT ("xxx")) != -1 + && new_argv.add (ACE_TEXT ("-p")) != -1 + && new_argv.add (ACE_TEXT ("Service_Config_Test.pid")) != -1 + && new_argv.add (ACE_TEXT ("-f")) != -1 + && new_argv.add (svc_conf) != -1); + + // We need this scope to make sure that the destructor for the + // <ACE_Service_Config> gets called. + ACE_Service_Config daemon; + + ACE_ASSERT (daemon.open (new_argv.argc (), + new_argv.argv ()) != -1 || errno == ENOENT); + + ACE_Time_Value tv (argc > 1 ? ACE_OS::atoi (argv[1]) : 2); + + ACE_ASSERT (ACE_Reactor::instance()->run_reactor_event_loop (tv) == 0); + + // Wait for all threads to complete. + ACE_Thread_Manager::instance ()->wait (); +} + + +void +testLoadingServiceConfFile (int argc, ACE_TCHAR *argv[]) +{ + ACE_ARGV new_argv; + +#if defined (ACE_USES_WCHAR) + // When using full Unicode support, use the version of the Service + // Configurator file appropriate to the platform. + // For example, Windows Unicode uses UTF-16. + // + // iconv(1) found on Linux and Solaris, for example, can + // be used to convert between encodings. + // + // Byte ordering is also an issue, so we should be + // generating this file on-the-fly from the UTF-8 encoded + // file by using functions like iconv(1) or iconv(3). +# if defined (ACE_WIN32) + const ACE_TCHAR svc_conf[] = + ACE_TEXT ("Service_Config_Test.UTF-16") + ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); +# else + const ACE_TCHAR svc_conf[] = + ACE_TEXT ("Service_Config_Test.WCHAR_T") + ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); +# endif /* ACE_WIN32 */ +#else + // ASCII (UTF-8) encoded Service Configurator file. + const ACE_TCHAR svc_conf[] = + ACE_TEXT ("Service_Config_Test") + ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); +#endif /* ACE_USES_WCHAR */ + + // Process the Service Configurator directives in this test's + ACE_ASSERT (new_argv.add (argv) != -1 + && new_argv.add (ACE_TEXT ("-f")) != -1 + && new_argv.add (svc_conf) != -1); + + // We need this scope to make sure that the destructor for the + // <ACE_Service_Config> gets called. + ACE_Service_Config daemon; + + ACE_ASSERT (daemon.open (new_argv.argc (), + new_argv.argv ()) != -1 || errno == ENOENT); + + ACE_Time_Value tv (argc > 1 ? ACE_OS::atoi (argv[1]) : 2); + + ACE_ASSERT (ACE_Reactor::instance()->run_reactor_event_loop (tv) == 0); + + // Wait for all threads to complete. + ACE_Thread_Manager::instance ()->wait (); +} + + +// @brief The size of a repository is pre-determined and can not be exceeded +void +testLimits (int , ACE_TCHAR *[]) +{ + static const ACE_TCHAR *svc_desc1 = +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + ACE_TEXT ("dynamic Test_Object_1_More Service_Object * ") + ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_1_More\"") +#else + ACE_TEXT ("<dynamic id=\"Test_Object_1_More\" type=\"Service_Object\">") + ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_1_More\"/>") + ACE_TEXT ("</dynamic>") +#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ + ; + + static const ACE_TCHAR *svc_desc2 = +#if (ACE_USES_CLASSIC_SVC_CONF == 1) + ACE_TEXT ("dynamic Test_Object_2_More Service_Object * ") + ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_2_More\"") +#else + ACE_TEXT ("<dynamic id=\"Test_Object_2_More\" type=\"Service_Object\">") + ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_2_More\"/>") + ACE_TEXT ("</dynamic>") +#endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ + ; + + + u_int error0 = error; + + // Ensure enough room for one in a own + ACE_Service_Gestalt one (1, true); + + // Add two. + // We cant simply rely on the fact that insertion fails, because it + // is typical to have no easy way of getting detailed error + // information from a parser. + one.process_directive (svc_desc1); + one.process_directive (svc_desc2); + + if (-1 == one.find (ACE_TEXT ("Test_Object_1_More"), 0, 0)) + { + ++error; + ACE_ERROR ((LM_ERROR, ACE_TEXT("Expected to have registered the first service\n"))); + } + + if (-1 != one.find (ACE_TEXT ("Test_Object_2_More"), 0, 0)) + { + ++error; + ACE_ERROR ((LM_ERROR, ACE_TEXT("Being able to add more than 1 service was not expected\n"))); + } + + if (error == error0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Limits test completed successfully\n"))); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Limits test failed\n"))); +} + + +// @brief ?? +void +testOrderlyInstantiation (int , ACE_TCHAR *[]) +{ + for (u_int i = 0; i < VARIETIES; ++i) + { + Test_Singleton *s = Test_Singleton::instance (i); + + if (s == 0) + { + ++error; + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("instance () allocate failed!\n"))); + } + } +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Service_Config_Test")); + + testOrderlyInstantiation (argc, argv); + testFailedServiceInit (argc, argv); + testLoadingServiceConfFile (argc, argv); + testLoadingServiceConfFileAndProcessNo (argc, argv); + testLimits (argc, argv); + + ACE_END_TEST; + return error == 0 ? 0 : 1; +} diff --git a/ACE/tests/Signal_Test.cpp b/ACE/tests/Signal_Test.cpp new file mode 100644 index 00000000000..26a10e2ef0a --- /dev/null +++ b/ACE/tests/Signal_Test.cpp @@ -0,0 +1,510 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Signal_Test.cpp +// +// = DESCRIPTION +// This program tests the signal handling capabilities of ACE on +// various OS platforms that support sending signals between +// processes. +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread_Manager.h" +#include "ace/Process.h" +#include "ace/Signal.h" +#include "ace/Get_Opt.h" +#include "ace/ARGV.h" +#include "ace/ACE.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Signal_Test, "$Id$") + +#if !defined (ACE_LACKS_UNIX_SIGNALS) + +// Global options. +static size_t n_iterations = 10000; + +// Keeps track of whether we're the child or not. +static int child = 0; + +// Keep track of the child pid. +static pid_t child_pid = 0; + +// Keep track of the (original) parent pid. +static pid_t parent_pid = 0; + +// Keep track of which test we're running. +static int test_number = 0; + +// Coordinate the shutdown between threads. +static sig_atomic_t shut_down = 0; + +static int +handle_signal (int signum) +{ + // ACE_DEBUG / ACE_ERROR invocations have been #if'd out because + // they are "unsafe" when handler is invoked asynchronously. On + // NetBSD 3.X, calls to change the thread's signal mask block as + // a lock seems to be held by the signal trampoline code. + +#if 0 + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) received signal %S\n"), + signum)); +#endif + + switch (signum) + { + case SIGCHLD: + // Signal to the main thread to shut down. + shut_down = 1; + + // This should only occur for the asynchronous case, so we don't + // need to return -1! + return 0; + case SIGINT: + /* FALLTHRU */ + case SIGTERM: + // Shut down our thread using <ACE_Thread_Manager::exit>. +#if 0 + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) shutting down due to %S\n"), + signum)); +#endif + + // Signal to the worker thread to shut down. + shut_down = 1; + + // Bail out and close down. + return -1; + /* NOTREACHED */ + + case SIGHUP: + { + // Shutdown the child. + +#if 0 + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) killing child pid %d \n"), + child_pid)); +#endif + int const result = ACE_OS::kill (child_pid, + SIGTERM); + ACE_ASSERT (result != -1); + + return -1; + } + /* NOTREACHED */ + case -1: +#if 0 + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + "sigwait")); +#endif + return -1; + /* NOTREACHED */ + default: +#if 0 + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) signal %S unexpected\n"), + signum)); +#endif + return -1; + /* NOTREACHED */ + } +} + +// This function handles signals synchronously. + +static ACE_THR_FUNC_RETURN +synchronous_signal_handler (void *) +{ + ACE_Sig_Set sigset; + + // Register signal handlers. + if (child) + { + sigset.sig_add (SIGINT); + sigset.sig_add (SIGTERM); + } + else + sigset.sig_add (SIGHUP); + + for (;;) + { + // Block waiting for SIGINT, SIGTERM, or SIGHUP, depending on + // whether we're the parent or child process. + int signr = ACE_OS::sigwait (sigset); + if (signr == -1) + { + if (errno != EINTR) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("sigwait"))); + continue; + } + if (handle_signal (signr) == -1) + break; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) handled signal\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) synchronous signal handler done\n"))); + + return 0; +} + +// This function arranges to handle signals asynchronously, which is +// necessary if an OS platform lacks threads. + +static ACE_THR_FUNC_RETURN +asynchronous_signal_handler (void *) +{ + ACE_Sig_Set sigset; + + // Register signal handlers. + if (child) + { + sigset.sig_add (SIGINT); + sigset.sig_add (SIGTERM); + } + else + { + sigset.sig_add (SIGCHLD); + sigset.sig_add (SIGHUP); + } + + // Register the <handle_signal> method to process all the signals in + // <sigset>. + ACE_Sig_Action sa (sigset, + (ACE_SignalHandler) handle_signal); + ACE_UNUSED_ARG (sa); + + return 0; +} + +// Function that runs in the child process in its own worker thread. + +static ACE_THR_FUNC_RETURN +worker_child (void *arg) +{ + long handle_signals_synchronously = + reinterpret_cast <long> (arg); + + for (size_t i = 0; i < n_iterations; i++) + { + if (shut_down > 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) we've been shutdown!\n"))); + break; + } + + // Every 100 iterations sleep for 2 seconds. + if ((i % 100) == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) sleeping for 2 seconds\n"))); + ACE_OS::sleep (2); + } + + // After 1000 iterations sent a SIGHUP to our parent. + if ((i % 1000) == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) sending SIGHUP to parent process %d\n"), + parent_pid)); + int const result = ACE_OS::kill (parent_pid, + SIGHUP); + if (result == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("kill"))); + ACE_ASSERT (result != -1); + } + } + } + + if (handle_signals_synchronously) + { + if (!shut_down) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) sending SIGINT to ourselves\n"))); + // We need to do this to dislodge the signal handling thread if + // it hasn't shut down on its own accord yet. + int const result = ACE_OS::kill (ACE_OS::getpid (), SIGINT); + ACE_ASSERT (result != -1); + } + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) finished running child\n"))); + return 0; +} + +// This function runs the parent process in a separate worker thread. +static ACE_THR_FUNC_RETURN +worker_parent (void *arg) +{ + long handle_signals_synchronously = + reinterpret_cast <long> (arg); + ACE_Process_Options options; + + ACE_TCHAR *l_argv[3]; + ACE_TCHAR pid_str[100]; + // Store the parent's process id so we can pass it to the child + // portably. Also, pass the test number, as well. + ACE_OS::sprintf (pid_str, + ACE_TEXT ("-p %ld -t %d"), + static_cast <long> (parent_pid), + test_number); + + // We're going to create a new process that runs this program again, + // so we need to indicate that it's the child. + const ACE_TCHAR *t = ACE_TEXT (".") + ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("Signal_Test") + ACE_PLATFORM_EXE_SUFFIX + ACE_TEXT (" -c"); + l_argv[0] = const_cast <ACE_TCHAR *> (t); + l_argv[1] = pid_str; + l_argv[2] = 0; + + ACE_ARGV argv (l_argv); + + // Generate a command-line! + options.command_line (argv.buf ()); + ACE_Process pm; + + child_pid = pm.spawn (options); + + if (child_pid == ACE_INVALID_PID) + ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) spawning child process failed\n"), 0); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawning child process %d\n"), + child_pid)); + + // Perform a <wait> until our child process has exited. + + if (handle_signals_synchronously) + { + int status; + // Wait for the child process to exit. + pm.wait (&status); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reaped child with status %d\n"), + status)); + } + else + while (shut_down == 0) + { + // Wait for a signal to arrive. + if (ACE_OS::sigsuspend (0) == -1 && errno != EINTR) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("sigsuspend"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) got signal!\n"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) parent worker done\n"))); + return 0; +} + +// This is the driver function that spawns threads to run the test for +// the parent and the child process. + +static void +run_test (ACE_THR_FUNC worker, + long handle_signals_in_separate_thread, + long handle_signals_synchronously) +{ +#if defined (ACE_HAS_THREADS) + if (handle_signals_synchronously) + { + // For the synchronous signal tests, block signals to prevent + // asynchronous delivery to default handler (at least necessary + // on linux and solaris; POSIX spec also states that signal(s) + // should be blocked before call to sigwait()) + ACE_Sig_Guard guard; + + int result; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawning worker thread\n"))); + result = ACE_Thread_Manager::instance ()->spawn + (worker, + reinterpret_cast <void *> (handle_signals_synchronously), + THR_DETACHED); + ACE_ASSERT (result != -1); + + if (handle_signals_in_separate_thread) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawning signal handler thread\n"))); + + result = ACE_Thread_Manager::instance ()->spawn + (synchronous_signal_handler, + 0, + THR_DETACHED); + ACE_ASSERT (result != -1); + } + else + { + synchronous_signal_handler (0); + } + + // Wait for the thread(s) to finish. + result = ACE_Thread_Manager::instance ()->wait (); + ACE_ASSERT (result != -1); + } + else +#else + // Don't remove this since otherwise some compilers give warnings + // when ACE_HAS_THREADS is disabled! + ACE_UNUSED_ARG (synchronous_signal_handler); +#endif /* ACE_HAS_THREADS */ + { + ACE_UNUSED_ARG (handle_signals_in_separate_thread); + // Arrange to handle signals asynchronously. + asynchronous_signal_handler (0); + (*worker) (reinterpret_cast <void *> (handle_signals_synchronously)); + } +} + +// Parse the command-line arguments and set options. + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:chp:t:")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'i': + n_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + child = 1; + break; + case 'p': + parent_pid = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + test_number = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': + default: + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) usage:\n") + ACE_TEXT ("-i <iterations>\n") + ACE_TEXT ("-c\n") + ACE_TEXT ("-p <parent_pid>\n") + ACE_TEXT ("-t <test_number>\n"))); + break; + } +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_OS::signal(SIGHUP, SIG_DFL); + + if (argc > 1) + { + ACE_APPEND_LOG (ACE_TEXT ("Signal_Test-child")); + parse_args (argc, argv); + + if (test_number == 1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** test 1: handle signals synchronously in separate thread\n"))); + + // First, handle signals synchronously in separate thread. + run_test (worker_child, 1, 1); + } + else if (test_number == 2) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** test 2: handle signals synchronously in this thread\n"))); + + // Next, handle signals synchronously in this thread. + run_test (worker_child, 0, 1); + } + else if (test_number == 3) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** test 3: handle signals asynchronously in this thread\n"))); + + // Finally, handle signals asynchronously in this thread. + run_test (worker_child, 0, 0); + } + + ACE_END_LOG; + } + else + { + ACE_START_TEST (ACE_TEXT ("Signal_Test")); + ACE_INIT_LOG (ACE_TEXT ("Signal_Test-child")); + + // We need to get the process id here to work around "features" + // of Linux threads... + parent_pid = ACE_OS::getpid (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** test 1: handle signals synchronously in a separate thread\n"))); + + ++test_number; + // Run the parent logic for the signal test, first by handling + // signals synchronously in a separate thread. + run_test (worker_parent, 1L, 1L); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** test 2: handle signals synchronously in this thread\n"))); + + ++test_number; + // And next by handling synchronously signals in this thread. + run_test (worker_parent, 0L, 1L); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) **** test 3: handle signals asynchronously in this thread\n"))); + + ++test_number; + // And finally by handling asynchronously signals in this thread. + run_test (worker_parent, 0L, 0L); + + ACE_END_TEST; + } + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Signal_Test")); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("The Unix Signals capability is not supported on this platform\n"))); + ACE_END_TEST; + return 0; +} +#endif /* !defined (ACE_LACKS_UNIX_SIGNALS) */ diff --git a/ACE/tests/Sigset_Ops_Test.cpp b/ACE/tests/Sigset_Ops_Test.cpp new file mode 100644 index 00000000000..df2eb5bb2ef --- /dev/null +++ b/ACE/tests/Sigset_Ops_Test.cpp @@ -0,0 +1,148 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Sigset_Ops_Test.cpp +// +// = DESCRIPTION +// This program tests the correctness of following functions. +// sigfillset(), sigemptyset(), sigaddset(), sigdelset(), +// sigismember(). +// +// = AUTHOR +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_errno.h" + +ACE_RCSID(tests, Sigset_Ops_Test, "$Id$") + +void +siglistset (sigset_t x, int *sigset, int can_miss = 0) +{ + bool empty = true; + int result = 0; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Signal (s) in the set = %08x:\n"), x)) ; + + for (int i = 1; i < ACE_NSIG; i++) + { + result = ACE_OS::sigismember (&x, i); + + if (result > 0) + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" %d\n"), i)) ; + empty = false; + } + else if (can_miss) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Be careful... Signal %d is not valid\n"), + i)); + result = 1; + } + ACE_ASSERT ((sigset [i] ? result > 0 : result <= 0)) ; + } + + if (empty) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Empty!!\n\n"))) ; + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\n\n"))) ; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Sigset_Ops_Test")); + +#if defined (ACE_LACKS_SIGSET) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%n uses ACE implementation of sigset* () functions.\n\n"))) ; +#else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%n uses platform's native sigset* () functions.\n\n"))) ; +#endif + + sigset_t x ; // examined sigset + int sigset [ACE_NSIG] ; // a comparison sigset + int i ; + int status = 0; // 0 is success, else fail code + + // Two test signal numbers. I choose these low value signals to + // avoid exceeding the ACE_NSIG range. + const int tsig1 = 5 ; + const int tsig2 = 8 ; + + // Testing sigfillset + ACE_OS::sigfillset (&x) ; + + // fill the comparison set + for (i = 0 ; i < ACE_NSIG ; i++) + sigset [i] = 1 ; + + // There's no guarantee that the valid signals are sequential without + // missed spots. For example, Red Hat Enterprise Linux 3 (any version + // with NPTL) does not include signal 32 in sigfillset() since it's + // reserved for kernel/nptl use. So, allow a miss in this check, but + // be prepared to check the log file for misses if signal capability seems + // odd if the test passes. + siglistset (x, sigset, 1) ; + + // testing sigemptyset + ACE_OS::sigemptyset (&x) ; + + // empty the comparison set + for (i = 0 ; i < ACE_NSIG ; i++) + sigset [i] = 0 ; + + siglistset (x, sigset) ; + + // add the first signal into set + ACE_OS::sigaddset (&x, tsig1) ; + sigset [tsig1] = 1 ; + siglistset (x, sigset) ; + + // add the second signal into set + ACE_OS::sigaddset (&x, tsig2) ; + sigset [tsig2] = 1 ; + siglistset (x, sigset) ; + + // then remove it + ACE_OS::sigdelset (&x, tsig1) ; + sigset [tsig1] = 0 ; + siglistset (x, sigset) ; + + // remove the second one + ACE_OS::sigdelset (&x, tsig2) ; + sigset [tsig2] = 0 ; + siglistset (x, sigset) ; + + // Now testing out of bound signal + if (ACE_OS::sigismember (&x, ACE_NSIG) >= 0) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Platform doesn't check for valid signal number.\n"))); + status = 1; + } + else if (ACE_OS::last_error () != EINVAL) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p.\n"), ACE_TEXT ("Expected status EINVAL; got"))); + status = 1; + } + + /* Skip this test at this moment + // Test if platform can catch invalid sigset error Currently, I can + // only think of passing a NULL ptr If you know other situations + // that fall into this catagory, please let me know. Thanks. + ACE_DEBUG ((LM_ERROR, ACE_TEXT ("Now testing invalid sigset. If your platform gets a \nsegmentation fault, then it doesn't check the error properly.\n"))) ; + + ACE_ASSERT (ACE_OS::sigfillset (0) < 0 && ACE_OS::last_error () == EFAULT) ; + */ + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/Simple_Message_Block_Test.cpp b/ACE/tests/Simple_Message_Block_Test.cpp new file mode 100644 index 00000000000..6a7eb841240 --- /dev/null +++ b/ACE/tests/Simple_Message_Block_Test.cpp @@ -0,0 +1,215 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Simple_Message_Block_Test.cpp +// +// = DESCRIPTION +// This test program is a torture test that illustrates how +// ACE_Message_Block reference counting works, how and when locks +// are used, how memory is managed, and how continuation chains +// of message blocks are made. Ideally used with purify :-) +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Message_Block.h" +#include "ace/Synch_Traits.h" +#include "ace/Lock_Adapter_T.h" +#include "ace/OS_NS_string.h" +#include "ace/Thread_Mutex.h" + +ACE_RCSID(tests, Simple_Message_Block_Test, "$Id$") + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Simple_Message_Block_Test")); + + { + // Checks normal stack deletes. + ACE_Message_Block mb; + } + + { + // Checks normal heap deletes. + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, ACE_Message_Block, -1); + mb->release (); + } + + { + // Checks continuation of message blocks on the stack. + ACE_Message_Block mb1 (1024); + ACE_Message_Block mb2 (1024); + + mb1.cont (&mb2); + } + + { + // Checks continuation of message blocks on the heap. + ACE_Message_Block *mb1; + ACE_Message_Block *mb2; + + ACE_NEW_RETURN (mb1, ACE_Message_Block (1024), -1); + ACE_NEW_RETURN (mb2, ACE_Message_Block (1024), -1); + + mb1->cont (mb2); + mb1->release (); + } + + // Same set of tests but with locking_strategy set. + { + ACE_Lock_Adapter <ACE_SYNCH_MUTEX> mutex; + ACE_Lock *lock = &mutex; + + { + // Checks normal stack deletes. + ACE_Message_Block mb; + mb.locking_strategy (lock); + } + + { + // Checks normal heap deletes. + ACE_Message_Block *mb; + ACE_NEW_RETURN (mb, ACE_Message_Block, -1); + mb->locking_strategy (lock); + mb->release (); + } + + { + // Checks continuation of message blocks on the stack with one + // lock strategy. + ACE_Message_Block mb1 (1024); + ACE_Message_Block mb2 (1024); + + mb1.locking_strategy (lock); + + mb1.cont (&mb2); + } + + { + // Checks continuation of message blocks on the heap with one + // lock strategy. + ACE_Message_Block *mb1; + ACE_Message_Block *mb2; + + ACE_NEW_RETURN (mb1, ACE_Message_Block (1024), -1); + ACE_NEW_RETURN (mb2, ACE_Message_Block (1024), -1); + + mb1->locking_strategy (lock); + + mb1->cont (mb2); + mb1->release (); + } + + { + // Checks continuation of message blocks on the stack with two + // lock strategy. + ACE_Message_Block mb1 (1024); + ACE_Message_Block mb2 (1024); + + mb1.locking_strategy (lock); + mb2.locking_strategy (lock); + + mb1.cont (&mb2); + } + + { + // Checks continuation of message blocks on the heap with two + // lock strategy + ACE_Message_Block *mb1; + ACE_Message_Block *mb2; + + ACE_NEW_RETURN (mb1, ACE_Message_Block (1024), -1); + ACE_NEW_RETURN (mb2, ACE_Message_Block (1024), -1); + + mb1->locking_strategy (lock); + mb2->locking_strategy (lock); + + mb1->cont (mb2); + mb1->release (); + } + + { + // Checks continuation of message blocks on the heap with two + // lock strategy where the second one is a duplicate of the + // first + ACE_Message_Block *mb1; + ACE_NEW_RETURN (mb1, ACE_Message_Block (1024), -1); + mb1->locking_strategy (lock); + + ACE_Message_Block *mb2 = mb1->duplicate (); + + mb1->cont (mb2); + mb1->release (); + } + } + + { + // Checks continuation of message blocks on the heap with two + // different lock strategies + + ACE_Lock_Adapter <ACE_SYNCH_MUTEX> lock1; + ACE_Lock_Adapter <ACE_SYNCH_MUTEX> lock2; + + ACE_Message_Block *mb1; + ACE_Message_Block *mb2; + + ACE_NEW_RETURN (mb1, ACE_Message_Block (1024), -1); + ACE_NEW_RETURN (mb2, ACE_Message_Block (1024), -1); + + mb1->locking_strategy (&lock1); + mb2->locking_strategy (&lock2); + + mb1->cont (mb2); + mb1->release (); + } + + { + // Checks failure of copy when "virtual" allocation (using mark) + // is too small + char message[]="abcdefghijklmnop"; + ACE_Message_Block mb1 (ACE_OS::strlen (message) + 1); + ACE_Message_Block mb2 (ACE_OS::strlen (message) + 1); + + // Resize mb2 so that we mark for use less than the allocated buffer + if (mb2.size (ACE_OS::strlen (message) + 1 - 10) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Resize test failed ..\n"))); + } + + // We expect this to succeed + if (mb1.copy (message, ACE_OS::strlen (message) + 1) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Copy test failed ..\n"))); + } + + // We expect this to fail + if (mb2.copy (message, ACE_OS::strlen (message) + 1) != -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Copy test succeeded when it should have failed ..\n"))); + } + + // We also expect this to fail + if (mb2.copy (message) != -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) Copy test succeeded when it should have failed ..\n"))); + } + } + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Svc_Handler_Test.cpp b/ACE/tests/Svc_Handler_Test.cpp new file mode 100644 index 00000000000..d7342808799 --- /dev/null +++ b/ACE/tests/Svc_Handler_Test.cpp @@ -0,0 +1,151 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Svc_Handler_Test.cpp +// +// = DESCRIPTION +// This tests illustrates the "buffering" strategy of the +// <ACE_Buffered_Svc_Handler>. This test also illustrates how the +// <ACE_FILE_IO> classes work. +// +// = AUTHORS +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/FILE_Connector.h" +#include "ace/Null_Condition.h" +#include "ace/Null_Mutex.h" +#include "ace/Svc_Handler.h" +#include "ace/Synch_Traits.h" + +ACE_RCSID(tests, Svc_Handler_Test, "$Id$") + +typedef ACE_Buffered_Svc_Handler <ACE_FILE_STREAM, ACE_NULL_SYNCH> SVC_HANDLER; + +static void +run_test (SVC_HANDLER &svc_handler, + size_t iterations) +{ + // Create a whole slew of message blocks and pass them to the + // <svc_handler>. + for (size_t i = 0; i < iterations; i++) + { + ACE_Message_Block *mb = 0; + ACE_NEW (mb, + ACE_Message_Block (sizeof (ACE_TEXT("hello ")))); + + ACE_Message_Block *cb1 = 0; + ACE_NEW (cb1, + ACE_Message_Block (sizeof (ACE_TEXT("there\n")))); + + ACE_Message_Block *cb2 = 0; + ACE_NEW (cb2, + ACE_Message_Block (sizeof (ACE_TEXT("there\n")))); + + mb->copy ("hello ", + ACE_OS::strlen (ACE_TEXT("hello "))); + cb1->copy ("there ", + ACE_OS::strlen (ACE_TEXT("there "))); + mb->cont (cb1); + cb2->copy ("doug\n", + ACE_OS::strlen (ACE_TEXT("doug\n"))); + cb1->cont (cb2); + + // Note that this is a buffered call! + if (svc_handler.put (mb) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("put"))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("final flush\n"))); + + // Make sure to flush everything out before we exit. + if (svc_handler.flush () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("flush"))); +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Svc_Handler_Test")); + { + size_t max_buffer_size = BUFSIZ; + size_t iterations = 10; + + if (argc > 1) + max_buffer_size = ACE_OS::atoi (argv[1]); + if (argc > 2) + iterations = ACE_OS::atoi (argv[2]); + + ACE_FILE_Connector connector; + ACE_FILE_IO file_io; + // Create a temporary filename. + ACE_FILE_Addr file (ACE_sap_any_cast (ACE_FILE_Addr &)); + + // Open up the temp file. + if (connector.connect (file_io, file) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("connect failed for %p\n"), + file.get_path_name ()), + 1); + + // Create the service handler and assign it <file_io> as its data + // sink. + SVC_HANDLER svc_handler (0, + 0, + 0, + max_buffer_size, + 0); + svc_handler.peer () = file_io; + + // Run the test. + run_test (svc_handler, iterations); + + file_io.close (); + + // Open up the temp file. + if (connector.connect (file_io, file) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("connect failed for %p\n"), + file.get_path_name ()), + 1); + char buf[ACE_MAXLOGMSGLEN + 1]; + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::VERBOSE_LITE); + + ACE_FILE_Info info; + file_io.get_info (info); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT("file size = %d\n"), info.size_)); + + for (ssize_t n_bytes; (n_bytes = file_io.recv (buf, ACE_MAXLOGMSGLEN)) > 0; ) + { + buf[n_bytes] = '\0'; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT("%s"), ACE_TEXT_CHAR_TO_TCHAR(buf))); + } + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT("\n"))); + + file_io.close (); + + if (file_io.unlink () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("unlink failed for %p\n"), + file.get_path_name ()), + 1); + } + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/TP_Reactor_Test.cpp b/ACE/tests/TP_Reactor_Test.cpp new file mode 100644 index 00000000000..b6e4884c890 --- /dev/null +++ b/ACE/tests/TP_Reactor_Test.cpp @@ -0,0 +1,1238 @@ +// $Id$ + +//============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// TPReactor_test.cpp +// +// = DESCRIPTION +// This program illustrates how the <ACE_TP_Reactor> can be used to +// implement an application that does various operations. +// usage: TP_Reactor_Test +// -n number threads in the TP_Reactor thread pool +// -d duplex mode 1 (full-duplex) vs. 0 (half-duplex) +// -p port to listen(Server)/connect(Client) +// -h host to connect (Client mode) +// -s number of sender's instances ( Client mode) +// -b run client and server (both modes ) at the same time +// -v log level +// 0 - log all messages +// 1 - log only errors and unusual cases +// -i time to run in seconds +// -u show this message +// +// The main differences between Thread_Pool_Reactor_Test.cpp and +// this test are: +// +// 1. Thread_Pool_Reactor_Test.cpp tests only handle_input() +// events on the server, whereas this one tests both handle_input() and +// handle_output() on both server and client, i.e., the receiver +// and sender are completely event-driven. +// +// 2. The receiver and sender in this test can work in full duplex +// mode, i.e., input and ouput events are processed independently. +// Half-duplex mode (request-reply) is also supported. +// +// This test is therefore a bit more stressful than the +// Thread_Pool_Reactor.cpp for the ACE_TP_Reactor since same +// thread pool is shared between client and server. +// +// This test is a "twin" of the Proactor_Test.cpp, so it can help for +// developers to provide independent of Reactor/Proactor solutions. +// +// = AUTHOR +// Alexander Libman <alibman@ihug.com.au>,<alexl@rumblgroup.com> +// +//============================================================================ + +#include "test_config.h" + +#if defined(ACE_HAS_THREADS) + +#include "TP_Reactor_Test.h" + +#include "ace/Signal.h" +#include "ace/Service_Config.h" +#include "ace/Get_Opt.h" + +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Synch_Traits.h" +#include "ace/Thread_Semaphore.h" + +ACE_RCSID(TPReactor, TPReactor_Test, "TPReactor_Test.cpp,v 1.27 2000/03/07 17:15:56 schmidt Exp") + +// Some debug helper functions +static int disable_signal (int sigmin, int sigmax); + +// both: 0 run client or server / depends on host +// != 0 run client and server +static int both = 0; + +// Host that we're connecting to. +static const ACE_TCHAR *host = 0; + +// number of Senders instances +static int senders = 1; + +// duplex mode: == 0 half-duplex +// != 0 full duplex +static int duplex = 0; + +// number threads in the TP_Reactor thread pool +static int threads = 1; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +// Log options +static int loglevel = 1; // 0 full , 1 only errors + +static const size_t MIN_TIME = 1; // min 1 sec +static const size_t MAX_TIME = 3600; // max 1 hour +static u_int seconds = 2; // default time to run - 2 seconds + +static char data[] = + "GET / HTTP/1.1\r\n" + "Accept: */*\r\n" + "Accept-Language: C++\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "User-Agent: TPReactor_Test/1.0 (non-compatible)\r\n" + "Connection: Keep-Alive\r\n" + "\r\n" ; + +// ************************************************************* + +class LogLocker +{ +public: + + LogLocker () { ACE_LOG_MSG->acquire (); } + virtual ~LogLocker () { ACE_LOG_MSG->release (); } +}; +// ************************************************************* + +/** + * @class MyTask + * + * MyTask plays role for TP_Reactor threads pool + * + * MyTask is ACE_Task resposible for: + * 1. Creation and deletion of TP_Reactor and TP_Reactor thread pool + * 2. Running TP_Reactor event loop + */ +class MyTask : public ACE_Task<ACE_MT_SYNCH> +{ +public: + MyTask (void): sem_ ((unsigned int) 0), + my_reactor_ (0) {} + + virtual ~MyTask () { stop (); } + + virtual int svc (void); + + int start (int num_threads); + int stop (void); + +private: + int create_reactor (void); + int delete_reactor (void); + + ACE_SYNCH_RECURSIVE_MUTEX lock_; + ACE_Thread_Semaphore sem_; + ACE_Reactor *my_reactor_; +}; + +int +MyTask::create_reactor (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + monitor, + this->lock_, + -1); + + ACE_ASSERT (this->my_reactor_ == 0); + + ACE_TP_Reactor * pImpl = 0; + + ACE_NEW_RETURN (pImpl,ACE_TP_Reactor, -1); + + ACE_NEW_RETURN (my_reactor_, + ACE_Reactor (pImpl ,1), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) Create TP_Reactor\n"))); + + ACE_Reactor::instance (this->my_reactor_); + + this->reactor (my_reactor_); + + return 0; +} + +int +MyTask::delete_reactor (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + monitor, + this->lock_, + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) Delete TP_Reactor\n"))); + + delete this->my_reactor_; + ACE_Reactor::instance ((ACE_Reactor *) 0); + this->my_reactor_ = 0; + this->reactor (0); + + return 0; +} + +int +MyTask::start (int num_threads) +{ + if (this->create_reactor () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to create reactor")), + -1); + + if (this->activate (THR_NEW_LWP, num_threads) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to activate thread pool")), + -1); + + for (; num_threads > 0 ; num_threads--) + sem_.acquire (); + + return 0; +} + + +int +MyTask::stop (void) +{ + if (this->my_reactor_ != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("End TP_Reactor event loop\n"))); + + ACE_Reactor::instance()->end_reactor_event_loop (); + } + + if (this->wait () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to stop thread pool"))); + + if (this->delete_reactor () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p.\n"), + ACE_TEXT ("unable to delete reactor"))); + + return 0; +} + +int +MyTask::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) MyTask started\n"))); + + // signal that we are ready + sem_.release (1); + + while (ACE_Reactor::instance()->reactor_event_loop_done () == 0) + ACE_Reactor::instance()->run_reactor_event_loop (); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) MyTask finished\n"))); + return 0; +} + +// ************************************************************* + +Acceptor::Acceptor (void) + : ACE_Acceptor<Receiver,ACE_SOCK_ACCEPTOR> ((ACE_Reactor *) 0), + sessions_ (0), + total_snd_(0), + total_rcv_(0), + total_w_ (0), + total_r_ (0) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + for (size_t i = 0; i < MAX_RECEIVERS; ++i) + this->list_receivers_[i] =0; +} + +Acceptor::~Acceptor (void) +{ + this->reactor (0); + stop (); +} + +void +Acceptor::stop (void) +{ + // this method can be called only after reactor event loop id done + // in all threads + + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + for (size_t i = 0; i < MAX_RECEIVERS; ++i) + { + delete this->list_receivers_[i]; + this->list_receivers_[i] =0; + } +} + +void +Acceptor::on_new_receiver (Receiver &rcvr) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + this->sessions_++; + this->list_receivers_[rcvr.index_] = & rcvr; + ACE_DEBUG ((LM_DEBUG, + "Receiver::CTOR sessions_=%d\n", + this->sessions_)); +} + +void +Acceptor::on_delete_receiver (Receiver &rcvr) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + this->sessions_--; + + this->total_snd_ += rcvr.get_total_snd (); + this->total_rcv_ += rcvr.get_total_rcv (); + this->total_w_ += rcvr.get_total_w (); + this->total_r_ += rcvr.get_total_r (); + + if (rcvr.index_ < MAX_RECEIVERS + && this->list_receivers_[rcvr.index_] == &rcvr) + this->list_receivers_[rcvr.index_] = 0; + + ACE_TCHAR bufs [256]; + ACE_TCHAR bufr [256]; + + ACE_OS::sprintf ( bufs , ACE_TEXT ("%ld(%ld)"), + rcvr.get_total_snd (), + rcvr.get_total_w () ); + + ACE_OS::sprintf ( bufr , ACE_TEXT ("%ld(%ld)"), + rcvr.get_total_rcv (), + rcvr.get_total_r ()); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Receiver::~DTOR index=%d snd=%s rcv=%s sessions_=%d\n"), + rcvr.index_, + bufs, + bufr, + this->sessions_)); +} + +int +Acceptor::start (const ACE_INET_Addr &addr) +{ + if (ACE_Acceptor<Receiver,ACE_SOCK_ACCEPTOR>::open (addr, + ACE_Reactor::instance (), + ACE_NONBLOCK) < 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "Acceptor::start () - open failed"), + 0); + return 1; +} + +int +Acceptor::make_svc_handler (Receiver *&sh) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + if (sessions_ >= MAX_RECEIVERS) + return -1; + + for (size_t i = 0; i < MAX_RECEIVERS; ++i) + if (this->list_receivers_ [i] == 0) + { + ACE_NEW_RETURN (sh, + Receiver (this , i), + -1); + return 0; + } + return -1; +} + +// ************************************************************* + +Receiver::Receiver (Acceptor * acceptor, size_t index) + : acceptor_ (acceptor), + index_ (index), + flg_mask_ (ACE_Event_Handler::NULL_MASK), + total_snd_(0), + total_rcv_(0), + total_w_ (0), + total_r_ (0) +{ + if (acceptor_ != 0) + acceptor_->on_new_receiver (*this); +} + + +Receiver::~Receiver (void) +{ + this->reactor (0); + if (acceptor_ != 0) + acceptor_->on_delete_receiver (*this); + + this->index_ = 0; + + for (; ;) + { + ACE_Time_Value tv = ACE_Time_Value::zero; + ACE_Message_Block *mb = 0; + + if (this->getq (mb, &tv) < 0) + break; + + ACE_Message_Block::release (mb); + } +} + +int +Receiver::check_destroy (void) +{ + if (flg_mask_ == ACE_Event_Handler::NULL_MASK) + return -1; + + return 0; +} + +int +Receiver::open (void *) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Reactor *TPReactor = ACE_Reactor::instance (); + + this->reactor (TPReactor); + + flg_mask_ = ACE_Event_Handler::NULL_MASK ; + + if (TPReactor->register_handler (this, flg_mask_) == -1) + return -1; + + initiate_io (ACE_Event_Handler::READ_MASK); + + return check_destroy (); +} + +int +Receiver::initiate_io (ACE_Reactor_Mask mask) +{ + if (ACE_BIT_ENABLED (flg_mask_, mask)) + return 0; + + if (ACE_Reactor::instance ()->schedule_wakeup (this, mask) == -1) + return -1; + + ACE_SET_BITS (flg_mask_, mask); + return 0; +} + +int +Receiver::terminate_io (ACE_Reactor_Mask mask) +{ + if (ACE_BIT_DISABLED (flg_mask_, mask)) + return 0; + + if (ACE_Reactor::instance ()->cancel_wakeup (this, mask) == -1) + return -1; + + ACE_CLR_BITS (flg_mask_, mask); + return 0; +} + +int +Receiver::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_Reactor *TPReactor = ACE_Reactor::instance (); + + TPReactor->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); // Don't call handle_close + this->reactor (0); + this->destroy (); + return 0; +} + +int +Receiver::handle_input (ACE_HANDLE h) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ), + -1); + + int err = 0; + ssize_t res = this->peer ().recv (mb->rd_ptr (), BUFSIZ-1); + + this->total_r_++; + + if (res >= 0) + { + mb->wr_ptr (res); + this->total_rcv_ += res; + } + else + err = errno ; + + mb->wr_ptr ()[0] = '\0'; + + if (loglevel == 0 || res <= 0 || err!= 0) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, "**** Receiver::handle_input () SessionId=%d****\n", index_)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", BUFSIZ)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", h)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transferred", res)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", err)); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", mb->rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, "**** end of message ****************\n")); + } + + if (err == EWOULDBLOCK) + { + err=0; + res=0; + return check_destroy (); + } + + if (err !=0 || res <= 0) + { + ACE_Message_Block::release (mb); + return -1; + } + + ACE_Time_Value tv = ACE_Time_Value::zero; + + int qcount = this->putq (mb, & tv); + + if (qcount <= 0) // failed to putq + { + ACE_Message_Block::release (mb); + return -1 ; + } + + int rc = 0; + + if (duplex == 0) // half-duplex , stop read + rc = this->terminate_io (ACE_Event_Handler::READ_MASK); + else // full duplex + { + if (qcount >= 20 ) // flow control, stop read + rc = this->terminate_io (ACE_Event_Handler::READ_MASK); + else + rc = this->initiate_io (ACE_Event_Handler::READ_MASK); + } + + if (rc == -1) + return -1; + + //initiate write + if (this->initiate_io (ACE_Event_Handler::WRITE_MASK) != 0) + return -1; + + return check_destroy (); +} + +int +Receiver::handle_output (ACE_HANDLE h) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Time_Value tv = ACE_Time_Value::zero; + ACE_Message_Block *mb = 0; + + int err = 0; + ssize_t res = 0; + size_t bytes = 0; + + int qcount = this->getq (mb, &tv); + + if (mb != 0) // qcount >= 0) + { + bytes = mb->length (); + res = this->peer ().send (mb->rd_ptr (), bytes); + + this->total_w_++; + + if (res < 0) + err = errno ; + else + this->total_snd_ += res; + + + if (loglevel == 0 || res <= 0 || err!= 0) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, "**** Receiver::handle_output () SessionId=%d****\n", index_)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", bytes)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", h)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transferred", res)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", err)); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", mb->rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, "**** end of message ****************\n")); + } + } + + ACE_Message_Block::release (mb); + + if (err != 0 || res < 0) + return -1; + + if (qcount <= 0) // no more message blocks in queue + { + if (this->terminate_io (ACE_Event_Handler::WRITE_MASK) != 0) + return -1; + + if (this->initiate_io (ACE_Event_Handler::READ_MASK) != 0) + return -1; + } + + return check_destroy (); +} + +// ************************************************************* + +Connector::Connector (void) + : ACE_Connector<Sender,ACE_SOCK_CONNECTOR> ((ACE_Reactor *) 0), + sessions_ (0), + total_snd_(0), + total_rcv_(0), + total_w_ (0), + total_r_ (0) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + for (size_t i = 0; i < MAX_SENDERS; ++i) + this->list_senders_[i] = 0; +} + +Connector::~Connector (void) +{ + this->reactor (0); + stop (); +} + +void +Connector::stop () +{ + // this method can be called only + // after reactor event loop id done + // in all threads + + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + for (size_t i = 0; i < MAX_SENDERS; ++i) + { + delete this->list_senders_[i]; + this->list_senders_[i] =0; + } +} + +void +Connector::on_new_sender (Sender & sndr) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + this->sessions_++; + this->list_senders_[sndr.index_] = &sndr; + ACE_DEBUG ((LM_DEBUG, + "Sender::CTOR sessions_=%d\n", + this->sessions_)); +} + +void +Connector::on_delete_sender (Sender & sndr) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + this->sessions_--; + this->total_snd_ += sndr.get_total_snd(); + this->total_rcv_ += sndr.get_total_rcv(); + this->total_w_ += sndr.get_total_w(); + this->total_r_ += sndr.get_total_r(); + + if (sndr.index_ < MAX_SENDERS + && this->list_senders_[sndr.index_] == &sndr) + this->list_senders_[sndr.index_] = 0; + + ACE_TCHAR bufs [256]; + ACE_TCHAR bufr [256]; + + ACE_OS::sprintf ( bufs , ACE_TEXT ("%ld(%ld)"), + sndr.get_total_snd(), + sndr.get_total_w() ); + + ACE_OS::sprintf ( bufr , ACE_TEXT ("%ld(%ld)"), + sndr.get_total_rcv(), + sndr.get_total_r() ); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Sender::~DTOR index=%d snd=%s rcv=%s sessions_=%d\n"), + sndr.index_, + bufs, + bufr, + this->sessions_)); + +} + +int +Connector::start (const ACE_INET_Addr & addr, int num) +{ + + if (ACE_Connector<Sender,ACE_SOCK_CONNECTOR>::open (ACE_Reactor::instance (), + ACE_NONBLOCK) < 0) + ACE_ERROR_RETURN + ((LM_ERROR, + "%p\n", + "Connector::start () - open failed"), + 0); + + int rc = 0; + + for (int i = 0 ; i < num ; i++) + { + Sender * sender = 0; + + if (ACE_Connector<Sender,ACE_SOCK_CONNECTOR>::connect (sender, addr) < 0) + ACE_ERROR_RETURN + ((LM_ERROR, + "%p\n", + "Connector::start () - connect failed"), + rc); + } + + return rc; +} + +int +Connector::make_svc_handler (Sender * & sh) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (this->mutex_); + + if (sessions_ >= MAX_SENDERS) + return -1; + + for (size_t i = 0; i < MAX_SENDERS; ++i) + if (this->list_senders_ [i] == 0) + { + ACE_NEW_RETURN (sh, + Sender (this , i), + -1); + return 0; + } + + return -1; +} + +// ************************************************************* + +Sender::Sender (Connector* connector, size_t index) + : connector_ (connector), + index_ (index), + flg_mask_ (ACE_Event_Handler::NULL_MASK), + total_snd_(0), + total_rcv_(0), + total_w_ (0), + total_r_ (0) +{ + if (connector_ != 0) + connector_->on_new_sender (*this); + + ACE_OS::sprintf (send_buf_ ,data); +} + + +Sender::~Sender (void) +{ + this->reactor (0); + if (connector_ != 0) + connector_->on_delete_sender (*this); + + this->index_ = 0; + + for (; ;) + { + ACE_Time_Value tv = ACE_Time_Value::zero; + ACE_Message_Block *mb = 0; + + if (this->getq (mb, &tv) < 0) + break; + + ACE_Message_Block::release (mb); + } +} + +int +Sender::check_destroy (void) +{ + if (flg_mask_ == ACE_Event_Handler::NULL_MASK) + return -1; + + return 0; +} + +int Sender::open (void *) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Reactor * TPReactor = ACE_Reactor::instance (); + + this->reactor (TPReactor); + + flg_mask_ = ACE_Event_Handler::NULL_MASK ; + + if (TPReactor->register_handler (this,flg_mask_) == -1) + return -1; + + if (this->initiate_write () == -1) + return -1; + + if (duplex != 0) + initiate_io (ACE_Event_Handler::READ_MASK); + + return check_destroy (); +} + +int +Sender::initiate_write (void) +{ + if ( this->msg_queue ()->message_count () < 20) // flow control + { + size_t nbytes = ACE_OS::strlen (send_buf_); + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (nbytes+8), + -1); + + mb->init (send_buf_, nbytes); + mb->rd_ptr (mb->base ()); + mb->wr_ptr (mb->base ()); + mb->wr_ptr (nbytes); + + ACE_Time_Value tv = ACE_Time_Value::zero; + + int qcount =this->putq (mb, & tv); + + if (qcount <= 0) + { + ACE_Message_Block::release (mb); + return -1; + } + } + + return initiate_io (ACE_Event_Handler::WRITE_MASK); +} + +int +Sender::initiate_io (ACE_Reactor_Mask mask) +{ + if (ACE_BIT_ENABLED (flg_mask_, mask)) + return 0; + + if (ACE_Reactor::instance ()->schedule_wakeup (this, mask) == -1) + return -1; + + ACE_SET_BITS (flg_mask_, mask); + return 0; +} + +int +Sender::terminate_io (ACE_Reactor_Mask mask) +{ + if (ACE_BIT_DISABLED (flg_mask_, mask)) + return 0; + + if (ACE_Reactor::instance ()->cancel_wakeup (this, mask) == -1) + return -1; + + ACE_CLR_BITS (flg_mask_, mask); + return 0; +} + +int +Sender::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_Reactor * TPReactor = ACE_Reactor::instance (); + + TPReactor->remove_handler (this, + ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL); // Don't call handle_close + this->reactor (0); + this->destroy (); + return 0; +} + +int +Sender::handle_input (ACE_HANDLE h) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ), + -1); + + int err = 0; + ssize_t res = this->peer ().recv (mb->rd_ptr (), + BUFSIZ-1); + this->total_r_++; + + if (res >= 0) + { + mb->wr_ptr (res); + this->total_rcv_ += res; + } + else + err = errno ; + + mb->wr_ptr ()[0] = '\0'; + + if (loglevel == 0 || res <= 0 || err!= 0) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, "**** Sender::handle_input () SessionId=%d****\n", index_)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", BUFSIZ)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", h)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transferred", res)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", err)); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", mb->rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, "**** end of message ****************\n")); + } + + ACE_Message_Block::release (mb); + + if (err == EWOULDBLOCK) + { + err=0; + res=0; + return check_destroy (); + } + + if (err !=0 || res <= 0) + return -1; + + int rc = 0; + + if (duplex != 0) // full duplex, continue read + rc = initiate_io (ACE_Event_Handler::READ_MASK); + else + rc = terminate_io (ACE_Event_Handler::READ_MASK); + + if (rc != 0) + return -1 ; + + rc = initiate_write (); + if (rc != 0) + return -1; + + return check_destroy (); +} + +int +Sender::handle_output (ACE_HANDLE h) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Time_Value tv = ACE_Time_Value::zero; + ACE_Message_Block *mb = 0; + + int err=0; + ssize_t res=0; + size_t bytes=0; + + int qcount = this->getq (mb , & tv); + + if (mb != 0) // qcount >= 0 + { + bytes = mb->length (); + res = this->peer ().send (mb->rd_ptr (), bytes); + + this->total_w_++; + + if (res < 0) + err = errno ; + else + this->total_snd_ += res; + + if (loglevel == 0 || res <= 0 || err!= 0) + { + LogLocker log_lock; + + ACE_DEBUG ((LM_DEBUG, "**** Sender::handle_output () SessionId=%d****\n", index_)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", bytes)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", h)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transferred", res)); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", err)); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", mb->rd_ptr ())); + ACE_DEBUG ((LM_DEBUG, "**** end of message ****************\n")); + } + } + + ACE_Message_Block::release (mb); + + if (err != 0 || res < 0) + return -1; + + int rc = 0; + + if (qcount <= 0) // no more message blocks in queue + { + if (duplex != 0 && // full duplex, continue write + (this->total_snd_ - this->total_rcv_ ) < 1024*32 ) // flow control + rc = initiate_write (); + else + rc = terminate_io (ACE_Event_Handler::WRITE_MASK); + + if (rc == -1) + return -1; + } + + rc = initiate_io (ACE_Event_Handler::READ_MASK); + if (rc == -1) + return -1; + + return check_destroy (); +} + + +// ************************************************************* +// Configuration helpers +// ************************************************************* +int +print_usage (int /* argc */, ACE_TCHAR *argv[]) +{ + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("\nusage: %s") + ACE_TEXT ("\n-n <number threads in the thread pool>") + ACE_TEXT ("\n-d <duplex mode 1-on/0-off>") + ACE_TEXT ("\n-p <port to listen/connect>") + ACE_TEXT ("\n-h <host> for Sender mode") + ACE_TEXT ("\n-s <number of sender's instances>") + ACE_TEXT ("\n-b run client and server at the same time") + ACE_TEXT ("\n-v log level") + ACE_TEXT ("\n 0 - log all messages") + ACE_TEXT ("\n 1 - log only errors and unusual cases") + ACE_TEXT ("\n-i time to run in seconds") + ACE_TEXT ("\n-u show this message") + ACE_TEXT ("\n"), + argv[0] + )); + return -1; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + if (argc == 1) // no arguments , so one button test + { + both = 1; // client and server simultaneosly + duplex = 1; // full duplex is on + host = ACE_TEXT ("localhost"); // server to connect + port = ACE_DEFAULT_SERVER_PORT; // port to connect/listen + threads = 3; // size of Proactor thread pool + senders = 20; // number of senders + loglevel = 1; // log level : 0 full/ 1 only errors + seconds = 20; // time to run in seconds +#if defined(SOMAXCONN) // The test is invalid if senders > SOMAXCONN + if(SOMAXCONN < senders) + senders = SOMAXCONN; +#endif + return 0; + } + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:n:p:d:h:s:v:ub")); + int c; + + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'i': // time to run + seconds = ACE_OS::atoi (get_opt.opt_arg()); + if (seconds < MIN_TIME) + seconds = MIN_TIME; + if (seconds > MAX_TIME) + seconds = MAX_TIME; + break; + case 'b': // both client and server + both = 1; + break; + case 'v': // log level + loglevel = ACE_OS::atoi (get_opt.opt_arg()); + break; + case 'd': // duplex + duplex = ACE_OS::atoi (get_opt.opt_arg()); + break; + case 'h': // host for sender + host = get_opt.opt_arg(); + break; + case 'p': // port number + port = ACE_OS::atoi (get_opt.opt_arg()); + break; + case 'n': // thread pool size + threads = ACE_OS::atoi (get_opt.opt_arg()); + break; + case 's': // number of senders + senders = ACE_OS::atoi (get_opt.opt_arg()); + if (size_t (senders) > MAX_SENDERS) + senders = MAX_SENDERS; + break; + case 'u': + default: + return print_usage (argc,argv); + } // switch + } // while + + return 0; +} + +static int +disable_signal (int sigmin, int sigmax) +{ +#if defined (ACE_HAS_PTHREADS_STD) && !defined (ACE_LACKS_PTHREAD_SIGMASK) + sigset_t signal_set; + if (ACE_OS::sigemptyset (&signal_set) == - 1) + ACE_ERROR ((LM_ERROR, + "Error: (%P | %t):%p\n", + "sigemptyset failed")); + + for (int i = sigmin; i <= sigmax; i++) + ACE_OS::sigaddset (&signal_set, i); + + // Put the <signal_set>. + if (ACE_OS::pthread_sigmask (SIG_BLOCK, &signal_set, 0) != 0) + ACE_ERROR ((LM_ERROR, + "Error: (%P | %t):%p\n", + "pthread_sigmask failed")); +#else + ACE_UNUSED_ARG(sigmin); + ACE_UNUSED_ARG(sigmax); +#endif /* ACE_HAS_PTHREADS_STD && !ACE_LACKS_PTHREAD_SIGMASK */ + + return 1; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("TP_Reactor_Test")); + +#if defined(ACE_HAS_THREADS) + if (::parse_args (argc, argv) == -1) + return -1; + + ::disable_signal (SIGPIPE, SIGPIPE); + + MyTask task1; + Acceptor acceptor; + Connector connector; + + if (task1.start (threads) == 0) + { + int rc = 0; + + ACE_INET_Addr addr (port); + if (both != 0 || host == 0) // Acceptor + rc += acceptor.start (addr); + + if (both != 0 || host != 0) + { + if (host == 0) + host = ACE_LOCALHOST; + + if (addr.set (port, host, 1, addr.get_type ()) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), host)); + rc += connector.start (addr, senders); + + } + + if (rc > 0) + ACE_OS::sleep (seconds); + } + + task1.stop (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\nNumber of Receivers objects = %d\n") + ACE_TEXT ("\nNumber of Sender objects = %d\n"), + acceptor.get_number_sessions (), + connector.get_number_sessions ())); + + // As Reactor event loop now is inactive it is safe to destroy all + // senders + + connector.stop (); + acceptor.stop (); + + //Print statistic + ACE_TCHAR bufs [256]; + ACE_TCHAR bufr [256]; + + ACE_OS::sprintf ( bufs , ACE_TEXT ("%ld(%ld)"), + connector.get_total_snd(), + connector.get_total_w() ); + + ACE_OS::sprintf ( bufr , ACE_TEXT ("%ld(%ld)"), + connector.get_total_rcv(), + connector.get_total_r() ); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Connector/Senders total bytes: snd=%s rcv=%s\n"), + bufs, + bufr + )); + + ACE_OS::sprintf ( bufs , ACE_TEXT ("%ld(%ld)"), + acceptor.get_total_snd(), + acceptor.get_total_w() ); + + ACE_OS::sprintf ( bufr , ACE_TEXT ("%ld(%ld)"), + acceptor.get_total_rcv(), + acceptor.get_total_r() ); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Acceptor/Receivers total bytes: snd=%s rcv=%s\n"), + bufs, + bufr + )); + +#else /* ACE_HAS_THREADS */ + ACE_UNUSED_ARG( argc ); + ACE_UNUSED_ARG( argv ); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + + return 0; +} diff --git a/ACE/tests/TP_Reactor_Test.h b/ACE/tests/TP_Reactor_Test.h new file mode 100644 index 00000000000..1955608b034 --- /dev/null +++ b/ACE/tests/TP_Reactor_Test.h @@ -0,0 +1,200 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// TP_Reactor_Test.h +// +// = DESCRIPTION +// Define class needed for generating templates. IBM C++ requires this to +// be in its own file for auto template instantiation. +// +// = AUTHOR +// Alexander Libman <alibman@ihug.com.au>,<alexl@rumblgroup.com> +// +// ============================================================================ + +#ifndef ACE_TESTS_TP_REACTOR_TEST_H +#define ACE_TESTS_TP_REACTOR_TEST_H + +#include "ace/INET_Addr.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Stream.h" +#include "ace/Acceptor.h" +#include "ace/Connector.h" +#include "ace/Svc_Handler.h" +#include "ace/Recursive_Thread_Mutex.h" + +const size_t MAX_SENDERS = 1000; +const size_t MAX_RECEIVERS = 1000; + + +// ************************************************************* +// Receiver and Acceptor +// ************************************************************* +// forward declaration +class Acceptor; + +class Receiver : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH> +{ + friend class Acceptor; +public: + + Receiver (Acceptor * acceptor=0, size_t index=MAX_RECEIVERS+1); + + ~Receiver (void); + + long get_total_snd (void) { return this->total_snd_; } + long get_total_rcv (void) { return this->total_rcv_; } + long get_total_w (void) { return this->total_w_; } + long get_total_r (void) { return this->total_r_; } + + // virtual from ACE_Svc_Handler<> + virtual int open (void * pVoid); + + // virtual from ACE_Event_Handler + virtual int handle_input (ACE_HANDLE h); + virtual int handle_output (ACE_HANDLE h); + virtual int handle_close (ACE_HANDLE h , ACE_Reactor_Mask mask); + +private: + int terminate_io (ACE_Reactor_Mask mask); + int initiate_io (ACE_Reactor_Mask mask); + int check_destroy (void); + + Acceptor * acceptor_; + size_t index_; + int flg_mask_; + + ACE_Recursive_Thread_Mutex mutex_; + long total_snd_; + long total_rcv_; + long total_w_; + long total_r_; +}; + +// ************************************************************* + +class Acceptor : public ACE_Acceptor<Receiver,ACE_SOCK_ACCEPTOR> +{ + friend class Receiver; +public: + size_t get_number_sessions (void) { return sessions_; } + long get_total_snd (void) { return this->total_snd_; } + long get_total_rcv (void) { return this->total_rcv_; } + long get_total_w (void) { return this->total_w_; } + long get_total_r (void) { return this->total_r_; } + + Acceptor (void); + virtual ~Acceptor (void); + + void stop (void); + int start (const ACE_INET_Addr & addr); + + // virtual from ACE_Acceptor<Receiver,ACE_SOCK_ACCEPTOR> + virtual int make_svc_handler (Receiver * & sh); + +private: + + ACE_Recursive_Thread_Mutex mutex_; + size_t sessions_; + Receiver *list_receivers_[MAX_RECEIVERS]; + long total_snd_; + long total_rcv_; + long total_w_; + long total_r_; + + void on_new_receiver (Receiver & rcvr); + void on_delete_receiver (Receiver & rcvr); +}; + + +// ******************************************* +// Sender +// ******************************************* + +class Connector; + +class Sender : public ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_MT_SYNCH> +{ + friend class Connector; + +public: + Sender (Connector * connector=0, size_t index=MAX_SENDERS+1); + + ~Sender (void); + + long get_total_snd (void) { return this->total_snd_; } + long get_total_rcv (void) { return this->total_rcv_; } + long get_total_w (void) { return this->total_w_; } + long get_total_r (void) { return this->total_r_; } + + // virtual from ACE_Svc_Handler<> + virtual int open (void * pVoid); + + // virtual from ACE_Event_Handler + virtual int handle_input (ACE_HANDLE h); + virtual int handle_output (ACE_HANDLE h); + virtual int handle_close (ACE_HANDLE h , ACE_Reactor_Mask mask); + +private: + int terminate_io (ACE_Reactor_Mask mask); + int initiate_io (ACE_Reactor_Mask mask); + int initiate_write (); + int check_destroy (void); + + Connector * connector_; + size_t index_; + int flg_mask_; + + ACE_Recursive_Thread_Mutex mutex_; + + char send_buf_ [1024]; + long total_snd_; + long total_rcv_; + long total_w_; + long total_r_; +}; + +// ************************************************************* + +class Connector: public ACE_Connector<Sender,ACE_SOCK_CONNECTOR> +{ + friend class Sender; +public: + size_t get_number_sessions (void) { return sessions_; } + long get_total_snd (void) { return this->total_snd_; } + long get_total_rcv (void) { return this->total_rcv_; } + long get_total_w (void) { return this->total_w_; } + long get_total_r (void) { return this->total_r_; } + + + Connector (); + virtual ~Connector (); + + void stop (); + int start (const ACE_INET_Addr & addr , int num); + + // virtual from ACE_Connector<> + virtual int make_svc_handler (Sender * & sh); + +private: + + ACE_Recursive_Thread_Mutex mutex_; + size_t sessions_; + Sender * list_senders_ [MAX_SENDERS]; + long total_snd_; + long total_rcv_; + long total_w_; + long total_r_; + + void on_new_sender (Sender & sndr); + void on_delete_sender (Sender & sndr); +}; + + +#endif /* ACE_TESTS_TP_REACTOR_TEST_H */ diff --git a/ACE/tests/TSS_Static_Test.cpp b/ACE/tests/TSS_Static_Test.cpp new file mode 100644 index 00000000000..40e0b6d9e57 --- /dev/null +++ b/ACE/tests/TSS_Static_Test.cpp @@ -0,0 +1,113 @@ +// $Id$ + +// ============================================================================ +// +// = FILENAME +// TSS_Static_Test.cpp +// +// = DESCRIPTION +// This program tests the interaction between TSS and thread keys +// created during static construction. VxWorks static construction +// is quite broken. This test is designed to test changes to work +// around a bug in the VxWorks loader that constructs static objects +// multiple times. It sounds hard to believe, but I've seen it! +// +// = AUTHOR +// Chad Elliott <elliott_c@ociweb.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/ACE.h" +#include "ace/Thread.h" + +ACE_RCSID(tests, TSS_Static_Test, "$Id$") + +#if defined (ACE_HAS_TSS_EMULATION) + +class Some_Object +{ +public: + Some_Object (void); + ~Some_Object (void); +}; + +Some_Object::Some_Object (void) +{ + ACE::init (); + + // Cause the ACE_Log_Msg to be constructed during static construction + ACE_DEBUG ((LM_DEBUG, "")); + + // Assign something to TSS during static construction + ACE_thread_key_t key; + if (ACE_Thread::keycreate (&key, 0) == 0) + { + ACE_Thread::setspecific (key, this); + } +} + + +Some_Object::~Some_Object (void) +{ + ACE::fini (); +} + + +static Some_Object sobject; + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("TSS_Static_Test")); + + int status = 0; + ACE_thread_key_t key; + if (ACE_Thread::keycreate (&key, 0) == 0) + { + void* specific = 0; + if (ACE_Thread::getspecific (key, &specific) == 0) + { + if (specific == 0) + { + ACE_DEBUG ((LM_DEBUG, "Got back pointer: %x from key: %d. " + "Good!\n", + (size_t)specific, key)); + } + else + { + ++status; + ACE_ERROR ((LM_ERROR, "Something (%x) was found in tss " + "slot %d.\n" + "Nothing should be stored in our " + "TSS slot!\n", + (size_t)specific, key)); + } + } + else + { + ++status; + ACE_ERROR ((LM_ERROR, "Unable to get the thread specific " + "storage.\n")); + } + } + else + { + ++status; + ACE_ERROR ((LM_ERROR, "Unable to create the thread specific " + "storage key.\n")); + } + ACE_END_TEST; + return status; +} +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("TSS_Static_Test")); + ACE_DEBUG ((LM_INFO, ACE_TEXT ("This test requires TSS Emulation.\n"))); + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_TSS_EMULATION */ + diff --git a/ACE/tests/TSS_Test.cpp b/ACE/tests/TSS_Test.cpp new file mode 100644 index 00000000000..8ee832ba9cd --- /dev/null +++ b/ACE/tests/TSS_Test.cpp @@ -0,0 +1,293 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// TSS_Test.cpp +// +// = DESCRIPTION +// This program tests thread specific storage of data. The ACE_TSS +// wrapper transparently ensures that the objects of this class +// will be placed in thread-specific storage. All calls on +// ACE_TSS::operator->() are delegated to the appropriate method +// in the Errno class. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Doug Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Guard_T.h" +#include "ace/Thread_Manager.h" +#include "ace/Thread_Mutex.h" +#include "ace/Signal.h" +#include "TSS_Test_Errno.h" + +ACE_RCSID(tests, TSS_Test, "$Id$") + +static u_int errors = 0; + +#if defined (ACE_HAS_THREADS) + +#if defined (ACE_DEFAULT_THREAD_KEYS) + // If ACE_DEFAULT_THREAD_KEYS is defined, it is probably + // set to a small value. So that the test doesn't run out + // of keys quickly in the first thread, set the number of + // ITERATIONS to be small as well. + static const int ITERATIONS = + (ACE_DEFAULT_THREAD_KEYS - ACE_MAX_THREADS) / (2 * ACE_MAX_THREADS) < 2 + ? 1 + : (ACE_DEFAULT_THREAD_KEYS - ACE_MAX_THREADS) / (2 * ACE_MAX_THREADS); +#else + // POSIX requires at least _POSIX_THREAD_KEYS_MAX (128) keys. 25 + // iterations with 4 worker threads should be sufficient to check + // the TSS wrappers without exceeding the minimum requirements. + + static const int ITERATIONS = 25; +#endif /* ACE_DEFAULT_THREAD_KEYS */ + +// Static variables. +int Errno::flags_; +int Errno::created_; +int Errno::deleted_; + +ACE_Thread_Mutex *Errno::lock_ = 0; + +// This is our thread-specific error handler . . . +// See comment below about why it's dynamically allocated. +static ACE_TSS<Errno> *tss_error; + +// This is for testing/demonstrating ACE_TSS_Type_Adapter. It's +// dynamically allocated to avoid static objects, also. +static ACE_TSS<ACE_TSS_Type_Adapter<u_int> > *u; + +// Serializes output. +static ACE_Thread_Mutex output_lock; + +extern "C" void +cleanup (void *ptr) +{ + // Don't do this: ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) in cleanup, ptr = %x\n"), ptr)); + // The Log_Msg buffer is a TSS object, too, and it may be gone! + // if you must say something here try: + // ACE_OS::fprintf (stderr, ACE_TEXT("(%d) in cleanup, ptr = %x\n"), ACE_Thread::self(), ptr); + // and this: + // operator delete (ptr); + // is nonsense when applied to a void *! (even tho the compilers accept it???? + delete static_cast <int *> (ptr); +} + +// This worker function is the entry point for each thread. + +static void * +worker (void *c) +{ + int count = *(static_cast<int*> (c)); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) worker, iterations = %d\n"), count)); + + ACE_thread_key_t key = ACE_OS::NULL_key; + int *ip = 0; + + // Make one key that will be available when the thread exits so that + // we'll have something to cleanup! + + if (ACE_Thread::keycreate (&key, cleanup) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p (no keys available)\n"), + ACE_TEXT ("ACE_Thread::keycreate"))); + return (void *) -1; + } + + ACE_NEW_RETURN (ip, int, 0); + + if (ACE_Thread::setspecific (key, (void *) ip) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::setspecific"))); + + for (int i = 0; i < count; i++) + { + if (ACE_Thread::keycreate (&key, cleanup) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p (no more keys)\n"), + ACE_TEXT ("ACE_Thread::keycreate"))); + break; + } + + ACE_NEW_RETURN (ip, int, 0); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) in worker at location 1, ") + ACE_TEXT ("key = %d, ip = %x\n"), + key, ip)); + + // Needed to work around (possibly broken?) strict aliasing warning in GCC. + void *v_ip (reinterpret_cast <void *> (ip)); + + if (ACE_Thread::setspecific (key, v_ip) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::setspecific"))); + + if (ACE_Thread::getspecific (key, &v_ip) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::getspecific"))); + + if (ACE_Thread::setspecific (key, (void *) 0) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::setspecific"))); + + // See comment in cleanup () above. + delete ip; + +#if defined (ACE_HAS_TSS_EMULATION) + if (ACE_Thread::keyfree (key) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::keyfree"))); +#endif /* ACE_HAS_TSS_EMULATION */ + + // Cause an error. + ACE_OS::read (ACE_INVALID_HANDLE, 0, 0); + + // The following two lines set the thread-specific state. + (*tss_error)->error (errno); + (*tss_error)->line (__LINE__); + + // This sets the static state (note how C++ makes it easy to do + // both). + (*tss_error)->flags (count); + + { + // Use the guard to serialize access + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, output_lock, 0)); + ACE_ASSERT ((*tss_error)->flags () == ITERATIONS); + } + + // Demonstrate use of ACE_TSS_Type_Adapter to wrap built-in + // types when used with ACE_TSS. See DESCRIPTION of template + // class ACE_TSS_Type_Adapter in ace/Synch_T.h for what this + // should look like. Unfortunately, some compilers have trouble + // with the implicit type conversions. Others have problems with + // the *explicit* type conversions. +#if !defined (ACE_HAS_BROKEN_EXPLICIT_TYPECAST_OPERATOR_INVOCATION) + (*u)->operator u_int & () = 37; + if ((*u)->operator u_int () != 37) + { + // Use the guard to serialize access to errors. + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, output_lock, + 0)); + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("use of ACE_TSS_Type_Adapter failed, value ") + ACE_TEXT ("is %u, it should be 37!\n"), + (*u)->operator u_int ())); + ++errors; + } +#endif /* !defined (ACE_HAS_BROKEN_EXPLICIT_TYPECAST_OPERATOR_INVOCATION) */ + +#if defined (ACE_HAS_TSS_EMULATION) + key = ACE_OS::NULL_key; + + if (ACE_Thread::keycreate (&key, cleanup) == -1) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p (no more keys)\n"), + ACE_TEXT ("ACE_Thread::keycreate"))); + break; + } + + ACE_NEW_RETURN (ip, int, 0); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) in worker at location 2, ") + ACE_TEXT ("key = %d, ip = %x\n"), + key, ip)); + + // Needed to work around (possibly broken?) strict aliasing warning in GCC. + void *v_ip2 (reinterpret_cast <void *> (ip)); + + if (ACE_Thread::setspecific (key, v_ip2) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::setspecific"))); + + if (ACE_Thread::getspecific (key, &v_ip2) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::getspecific"))); + + if (ACE_Thread::setspecific (key, (void *) 0) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::setspecific"))); + + // See comment in cleanup () above. + delete ip; + + // ACE_HAS_TSS_EMULATION is turned on, then it should work. +# if defined (ACE_HAS_TSS_EMULATION) + if (ACE_Thread::keyfree (key) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("ACE_Thread::keyfree"))); +# endif /* defined (ACE_HAS_TSS_EMULATION) */ +#endif /* ACE_HAS_TSS_EMULATION */ + } + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("TSS_Test")); + +#if defined (ACE_HAS_THREADS) + Errno::allocate_lock (); + + const u_int threads = ACE_MAX_THREADS; + + // Dynamically allocate TSS_Error so that we can control when it + // gets deleted. Specifically, we need to delete it before the + // ACE_Object_Manager destroys the ACE_Allocator. That's because + // deletion of TSS_Error causes the internal structures of + // ACE_TSS_Cleanup to be modified, and which in turn uses + // ACE_Allocator. + ACE_NEW_RETURN (tss_error, ACE_TSS<Errno>, 1); + + // Similarly, dynamically allocate u. + ACE_NEW_RETURN (u, ACE_TSS<ACE_TSS_Type_Adapter<u_int> >, 1); + + int iterations = ITERATIONS; + if (ACE_Thread_Manager::instance ()->spawn_n + (threads, + ACE_THR_FUNC (worker), + &iterations, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), ACE_TEXT ("spawn_n")), 1); + + ACE_Thread_Manager::instance ()->wait (); + + delete u; + delete tss_error; + + Errno::deallocate_lock (); + + + if (Errno::created () != Errno::deleted ()) + { + //@@TODO: this should probably be promoted to an error rather than just a + // warning. + ACE_ERROR ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Warning: Number created (%d) != number deleted (%d)\n"), + Errno::created (), + Errno::deleted () + )); + } + +#else /* ACE_HAS_THREADS */ + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads are not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return errors ? -1 : 0; +} diff --git a/ACE/tests/TSS_Test_Errno.h b/ACE/tests/TSS_Test_Errno.h new file mode 100644 index 00000000000..7d822a7099b --- /dev/null +++ b/ACE/tests/TSS_Test_Errno.h @@ -0,0 +1,102 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = FILENAME +// TSS_Test_Errno.h +// +// = DESCRIPTION +// This file contains the definition of Errno. Some compilers need +// it in a .h file for template instantiation (such as AIX C Set +// ++). +// +// = AUTHOR +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/Guard_T.h" +#include "ace/Thread_Mutex.h" + +class Errno +{ + // = TITLE + // Define a simple Errno abstraction + // + // = DESCRIPTION + // This class gets its own header file to work around AIX C++ + // compiler "features" related to template instantiation... It is + // only used by TSS_Test.cpp. +public: + Errno() + { + ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_Mon, *Errno::lock_)); + created_ += 1; + } + ~Errno() + { + ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_Mon, *Errno::lock_)); + deleted_ += 1; + } + + int error (void) { return this->errno_; } + void error (int i) { this->errno_ = i; } + + int line (void) { return this->lineno_; } + void line (int l) { this->lineno_ = l; } + + // Errno::flags_ is a static variable, so we've got to protect it + // with a mutex since it isn't kept in thread-specific storage. + int flags (void) + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_Mon, *Errno::lock_, -1)); + + return Errno::flags_; + } + int flags (int f) + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, *Errno::lock_, -1)); + + Errno::flags_ = f; + return 0; + } + + static int created (void) + { + return created_; + } + + static int deleted (void) + { + return deleted_; + } + +#if defined (ACE_HAS_THREADS) + static + ACE_Thread_Mutex * + allocate_lock (void) + { + ACE_NEW_RETURN (Errno::lock_, ACE_Thread_Mutex, 0); + return Errno::lock_; + } + + static + void + deallocate_lock () { delete lock_; lock_ = 0; } +#endif /* ACE_HAS_THREADS */ + +private: + // = errno_ and lineno_ will be thread-specific data so they don't + // need a lock. + int errno_; + int lineno_; + + static int flags_; + static int created_; + static int deleted_; +#if defined (ACE_HAS_THREADS) + // flags_ needs a lock. + static ACE_Thread_Mutex *lock_; +#endif /* ACE_HAS_THREADS */ +}; diff --git a/ACE/tests/Task_Ex_Test.cpp b/ACE/tests/Task_Ex_Test.cpp new file mode 100644 index 00000000000..232e9b23c4e --- /dev/null +++ b/ACE/tests/Task_Ex_Test.cpp @@ -0,0 +1,165 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Task_Ex_Test.cpp +// +// = DESCRIPTION +// This test program illustrates the ACE_Task_Ex class which has the ACE_Message_Queue_Ex +// that has the capability to hold user-defined messages instead of ACE_Message_Block +// +// +// = AUTHOR +// Kobi Cohen-Arazi <kobi-co@barak-online.net> +// +// ============================================================================ + +#include "test_config.h" +#include "Task_Ex_Test.h" +#include "ace/Task_Ex_T.h" +#include "ace/Log_Msg.h" +#include "ace/Auto_Ptr.h" + +ACE_RCSID(tests, Task_Test_Ex, "$Id$") + +#if defined (ACE_HAS_THREADS) + +/// default params +#if defined (ACE_VXWORKS) +// this is a very expensive test on VxWorks so limit it otherwise it will never finish in time:-) +const ACE_INT32 PRODUCER_THREADS_NO=10; +const ACE_INT32 CONSUMER_THREADS_NO=10; +const ACE_INT32 NUMBER_OF_MSGS=200; +#else +const ACE_INT32 PRODUCER_THREADS_NO=20; +const ACE_INT32 CONSUMER_THREADS_NO=20; +const ACE_INT32 NUMBER_OF_MSGS=2000; +#endif + +/// @class Consumer consumes user defined Msgs +class Consumer : public ACE_Task_Ex<ACE_MT_SYNCH, User_Defined_Msg> +{ +public: + //FUZZ: disable check_for_lack_ACE_OS + /// activate/spawn the threads. + int open (void*); + //FUZZ: enable check_for_lack_ACE_OS + + /// svc thread entry point + virtual int svc (void); +private: + +}; + +int Consumer::open (void*) +{ + if(this->activate (THR_NEW_LWP | THR_JOINABLE, + CONSUMER_THREADS_NO)==-1) + { + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT("Consumer::open Error spanwing thread %p\n"), + "err="), + -1); + } + return 0; +} + +int Consumer::svc () +{ + User_Defined_Msg* pMsg=0; + while(this->getq (pMsg)!=-1) + { + ACE_ASSERT (pMsg!=0); + auto_ptr<User_Defined_Msg> pAuto(pMsg); + ACE_DEBUG((LM_DEBUG, + ACE_TEXT("Consumer::svc got msg id=%d\n"), + pMsg->msg_id ())); + if(pMsg->msg_id ()==NUMBER_OF_MSGS-1) + break; + } + + ACE_DEBUG((LM_INFO, + ACE_TEXT("Consumer::svc ended thread %t\n"))); + + return 0; +} + + +/// producer function produces user defined messages. +ACE_THR_FUNC_RETURN producer (void *arg) +{ + Consumer* c = static_cast<Consumer*> (arg); + ACE_ASSERT(c!=0); + if (c==0) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("producer Error casting to consumer\n"))); + return (ACE_THR_FUNC_RETURN)-1; + } + for (int i=0;i!=NUMBER_OF_MSGS;++i) + { + User_Defined_Msg* pMsg=0; + ACE_NEW_NORETURN(pMsg, User_Defined_Msg(i)); + if (pMsg==0) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("producer Error allocating data %p\n"), + "err=")); + return (ACE_THR_FUNC_RETURN)-1; + } + if(c->putq (pMsg)==-1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("producer Error putq data %p\n"), + "err=")); + return (ACE_THR_FUNC_RETURN)-1; + } + } + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Task_Ex_Test")); + +#if defined (ACE_HAS_THREADS) + + Consumer c; + if(c.open (0)==-1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("main Error opening consumer\n")),-1); + + + int result=ACE_Thread_Manager::instance()->spawn_n (PRODUCER_THREADS_NO, + ACE_THR_FUNC(producer), + static_cast<void*> (&c)); + if (result==-1) + { + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("main Error spawning threads %p\n"), + "err="),-1); + } + + // wait all threads + int wait_result=ACE_Thread_Manager::instance()->wait(); + if (wait_result==-1) + { + ACE_ERROR((LM_ERROR, + ACE_TEXT("main Error Thread_Manager->wait %p\n"), + "err=")); + return -1; + } +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Task_Ex_Test.h b/ACE/tests/Task_Ex_Test.h new file mode 100644 index 00000000000..c7092a5e808 --- /dev/null +++ b/ACE/tests/Task_Ex_Test.h @@ -0,0 +1,34 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Task_Ex_Test.h + * + * $Id$ + * + * Define class needed for generating templates. IBM C++ requires this + * to be in its own file for auto template instantiation. + * + * @author Kobi Cohen-Arazi <kobi-co@barak-online.net> + */ +//============================================================================= + +#ifndef ACE_TESTS_TASK_EX_TEST_H +#define ACE_TESTS_TASK_EX_TEST_H + +#include "ace/Basic_Types.h" + +class User_Defined_Msg +{ +public: + /// c'tor sets the msg id + User_Defined_Msg (ACE_INT32 id) : msg_id_ (id) + {} + /// accessors to msg_id_ + ACE_INT32 msg_id () const {return msg_id_;} +private: + /// keep the msg id here + ACE_INT32 msg_id_; +}; + +#endif /* ACE_TESTS_TASK_EX_TEST_H */ diff --git a/ACE/tests/Task_Test.cpp b/ACE/tests/Task_Test.cpp new file mode 100644 index 00000000000..24bb021e1bd --- /dev/null +++ b/ACE/tests/Task_Test.cpp @@ -0,0 +1,159 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Task_Test.cpp +// +// = DESCRIPTION +// This test program illustrates how the ACE barrier +// synchronization mechanisms work in conjunction with the +// <ACE_Task> and the <ACE_Thread_Manager>. This also illustrates +// how the <ACE_Thread_Hook> mechanism works. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and +// Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Task.h" +#include "ace/Thread_Hook.h" + +ACE_RCSID(tests, Task_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) +#include "ace/Atomic_Op.h" +#include "ace/Barrier.h" + +// Make sure that only one thread sees the "time to clean up" condition +// in Barrier_Task::close() +static ACE_Atomic_Op<ACE_Thread_Mutex, int> close_cleanups (0); + +class My_Thread_Hook : public ACE_Thread_Hook +{ +public: + virtual ACE_THR_FUNC_RETURN start (ACE_THR_FUNC func, + void *arg); +}; + +class Barrier_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Barrier_Task (ACE_Thread_Manager *thr_mgr, + int n_threads, + int n_iterations); + + //FUZZ: disable check_for_lack_ACE_OS + virtual int close (u_long flags = 0); + //FUZZ: enable check_for_lack_ACE_OS + + virtual int svc (void); + // Iterate <n_iterations> time printing off a message and "waiting" + // for all other threads to complete this iteration. + +private: + ACE_Barrier barrier_; + // Reference to the tester barrier. This controls each iteration of + // the tester function running in every thread. + + int n_iterations_; + // Number of iterations to run. +}; + +ACE_THR_FUNC_RETURN +My_Thread_Hook::start (ACE_THR_FUNC func, + void *arg) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) starting the thread!\n"))); + return (func) (arg); +} + +Barrier_Task::Barrier_Task (ACE_Thread_Manager *thr_mgr, + int n_threads, + int n_iterations) + : ACE_Task<ACE_MT_SYNCH> (thr_mgr), + barrier_ (n_threads), + n_iterations_ (n_iterations) +{ + // Create worker threads. + if (this->activate (THR_NEW_LWP, n_threads) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("activate failed"))); +} + +// Check to see if it's time to clean up by examining last_thread(). +int +Barrier_Task::close (u_long) +{ + if (ACE_OS::thr_equal (ACE_Thread::self (), + this->last_thread ())) + { + ++close_cleanups; + } + return 0; +} + +// Iterate <n_iterations> time printing off a message and "waiting" +// for all other threads to complete this iteration. +int +Barrier_Task::svc (void) +{ + for (int iterations = 1; + iterations <= this->n_iterations_; + iterations++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) in iteration %d\n"), + iterations)); + + // Block until all other threads have waited, then continue. + this->barrier_.wait (); + } + + // Note that the <ACE_Task::svc_run> method automatically removes us + // from the Thread_Manager when the thread exits. + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Task_Test")); + +#if defined (ACE_HAS_THREADS) + // Set the thread hook! + ACE_Thread_Hook::thread_hook (new My_Thread_Hook); + + int n_threads = ACE_MAX_THREADS; + int n_iterations = ACE_MAX_ITERATIONS; + + Barrier_Task barrier_task (ACE_Thread_Manager::instance (), + n_threads, + n_iterations); + + ACE_Thread_Manager::instance ()->wait (); + + // Only one of the threads should see a cleanup... + if (close_cleanups != 1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%d threads saw cleanup indication; ") + ACE_TEXT ("should be 1\n"), + close_cleanups.value ())); + + // Cleanup the thread hook so it doesn't leak. + delete ACE_Thread_Hook::thread_hook (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Test_Output.cpp b/ACE/tests/Test_Output.cpp new file mode 100644 index 00000000000..fbeede7a8cc --- /dev/null +++ b/ACE/tests/Test_Output.cpp @@ -0,0 +1,224 @@ +// -*- C++ -*- + +// ============================================================================ +/** + * @file Test_Output.cpp + * + * $Id$ + * + * This file factors out common macros and other utilities used by the + * ACE automated regression tests. + * + * @author Prashant Jain <pjain@cs.wustl.edu> + * @author Tim Harrison <harrison@cs.wustl.edu> + * @author David Levine <levine@cs.wustl.edu> + * @author Don Hinton <dhinton@dresystems.com> + */ +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/Guard_T.h" +#include "ace/Object_Manager.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/Framework_Component.h" +#include "ace/Log_Msg.h" +#include "ace/ACE.h" + +#if defined (VXWORKS) +# include "ace/OS_NS_unistd.h" +# include "ace/OS_NS_fcntl.h" +#endif /* VXWORKS */ + +ACE_Test_Output *ACE_Test_Output::instance_ = 0; + +ACE_Test_Output::ACE_Test_Output (void) + : output_file_ (0) +{ +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + this->output_file_ = new OFSTREAM; +#endif /* ACE_LACKS_IOSTREAM_TOTALLY */ +} + +ACE_Test_Output::~ACE_Test_Output (void) +{ +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + ACE_LOG_MSG->msg_ostream (&cerr, 0); +#endif /* ! ACE_LACKS_IOSTREAM_TOTALLY */ + + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::OSTREAM); + ACE_LOG_MSG->set_flags (ACE_Log_Msg::STDERR); + +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) && \ + (!defined (ACE_HAS_PHARLAP) || defined (ACE_PHARLAP_TESTLOG_TO_FILE)) + delete this->output_file_; +#endif /* ! ACE_LACKS_IOSTREAM_TOTALLY */ +} + +OFSTREAM * +ACE_Test_Output::output_file (void) +{ + // the output_file_ is loaned to ACE_LOG_MSG + // and something else might destroy and/or change the stream + // so return what ACE_LOG_MSG is using. +#if defined (ACE_LACKS_IOSTREAM_TOTALLY) + return reinterpret_cast<OFSTREAM*>(ACE_LOG_MSG->msg_ostream ()); +#else + return dynamic_cast<OFSTREAM*>(ACE_LOG_MSG->msg_ostream ()); +#endif /* ACE_LACKS_IOSTREAM_TOTALLY */ +} + +int +ACE_Test_Output::set_output (const ACE_TCHAR *filename, int append) +{ +#if defined (ACE_HAS_PHARLAP) && !defined (ACE_PHARLAP_TESTLOG_TO_FILE) + // For PharLap, just send it all to the host console for now - redirect + // to a file there for saving/analysis. + EtsSelectConsole(ETS_CO_HOST); + ACE_LOG_MSG->msg_ostream (&cout); + +#else + ACE_TCHAR temp[MAXPATHLEN + 1] = { 0 }; + // Ignore the error value since the directory may already exist. + const ACE_TCHAR *test_dir; + +#if !defined (ACE_HAS_WINCE) +# if defined (ACE_WIN32) || !defined (ACE_USES_WCHAR) + test_dir = ACE_OS::getenv (ACE_TEXT ("ACE_TEST_DIR")); +# else + ACE_TCHAR tempenv[MAXPATHLEN + 1] = { 0 }; + char const * const test_dir_n = ACE_OS::getenv ("ACE_TEST_DIR"); + if (test_dir_n == 0) + test_dir = 0; + else + { + ACE_OS::strncpy (tempenv, + ACE_TEXT_CHAR_TO_TCHAR (test_dir_n), + MAXPATHLEN); + test_dir = tempenv; + } +# endif /* ACE_WIN32 || !ACE_USES_WCHAR */ + + if (test_dir == 0) +#endif /* ACE_HAS_WINCE */ + test_dir = ACE_TEXT (""); + + // This could be done with ACE_OS::sprintf() but it requires different + // format strings for wide-char POSIX vs. narrow-char POSIX and Windows. + // Easier to keep straight like this. + ACE_OS::strncpy (temp, test_dir, MAXPATHLEN); + ACE_OS::strcat (temp, ACE_LOG_DIRECTORY); + ACE_OS::strcat (temp, + ACE::basename (filename, ACE_DIRECTORY_SEPARATOR_CHAR)); + ACE_OS::strcat (temp, ACE_LOG_FILE_EXT_NAME); + +#if defined (VXWORKS) + // This is the only way I could figure out to avoid a console + // warning about opening an existing file (w/o O_CREAT), or + // attempting to unlink a non-existant one. + ACE_HANDLE fd = ACE_OS::open (temp, + O_WRONLY|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (fd != ERROR) + { + ACE_OS::close (fd); + ACE_OS::unlink (temp); + } +# else /* ! VXWORKS */ + // This doesn't seem to work on VxWorks if the directory doesn't + // exist: it creates a plain file instead of a directory. If the + // directory does exist, it causes a wierd console error message + // about "cat: input error on standard input: Is a directory". So, + // VxWorks users must create the directory manually. +# if defined (ACE_HAS_WINCE) + ACE_OS::mkdir (ACE_LOG_DIRECTORY_FOR_MKDIR); +# else + ACE_OS::mkdir (ACE_LOG_DIRECTORY); +# endif // ACE_HAS_WINCE +# endif /* ! VXWORKS */ + +# if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + this->output_file_->open (ACE_TEXT_ALWAYS_CHAR (temp), + ios::out | (append ? ios::app : ios::trunc)); + if (this->output_file_->bad ()) + return -1; +#else /* when ACE_LACKS_IOSTREAM_TOTALLY */ + ACE_TCHAR *fmode = 0; + if (append) + fmode = ACE_TEXT ("a"); + else + fmode = ACE_TEXT ("w"); + this->output_file_ = ACE_OS::fopen (temp, fmode); +# endif /* ACE_LACKS_IOSTREAM_TOTALLY */ + + ACE_LOG_MSG->msg_ostream (this->output_file_, 0); +#endif /* ACE_HAS_PHARLAP && !ACE_PHARLAP_TESTLOG_TO_FILE */ + + ACE_LOG_MSG->clr_flags (ACE_Log_Msg::STDERR | ACE_Log_Msg::LOGGER ); + ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM); + + return 0; +} + +void +ACE_Test_Output::close (void) +{ + if (this->output_file_ && + (this->output_file_ == ACE_LOG_MSG->msg_ostream ())) + { +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + this->output_file_->flush (); + this->output_file_->close (); +#else + ACE_OS::fflush (this->output_file_); + ACE_OS::fclose (this->output_file_); +#endif /* !ACE_LACKS_IOSTREAM_TOTALLY */ + ACE_LOG_MSG->msg_ostream (0, 0); + } + // else something else changed the stream and hence should + // have closed the output_file_ +} + +ACE_Test_Output* +ACE_Test_Output::instance () +{ + if (ACE_Test_Output::instance_ == 0) + { + // Perform Double-Checked Locking Optimization. + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + if (ACE_Test_Output::instance_ == 0) + { + ACE_NEW_RETURN (ACE_Test_Output::instance_, + ACE_Test_Output, + 0); + ACE_REGISTER_FRAMEWORK_COMPONENT(ACE_Test_Output, ACE_Test_Output::instance_) + } + } + return ACE_Test_Output::instance_; +} + +const ACE_TCHAR * +ACE_Test_Output::dll_name (void) +{ + return ACE_TEXT ("Test_Output"); +} + +const ACE_TCHAR * +ACE_Test_Output::name (void) +{ + return ACE_TEXT ("ACE_Test_Output"); +} + +void +ACE_Test_Output::close_singleton (void) +{ + delete ACE_Test_Output::instance_; + ACE_Test_Output::instance_ = 0; +} diff --git a/ACE/tests/Test_Output_Export.h b/ACE/tests/Test_Output_Export.h new file mode 100644 index 00000000000..4f6ec2f7cb5 --- /dev/null +++ b/ACE/tests/Test_Output_Export.h @@ -0,0 +1,54 @@ + +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. +// This file is generated automatically by generate_export_file.pl Test_Output +// ------------------------------ +#ifndef TEST_OUTPUT_EXPORT_H +#define TEST_OUTPUT_EXPORT_H + +#include "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (TEST_OUTPUT_HAS_DLL) +# define TEST_OUTPUT_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && TEST_OUTPUT_HAS_DLL */ + +#if !defined (TEST_OUTPUT_HAS_DLL) +# define TEST_OUTPUT_HAS_DLL 1 +#endif /* ! TEST_OUTPUT_HAS_DLL */ + +#if defined (TEST_OUTPUT_HAS_DLL) && (TEST_OUTPUT_HAS_DLL == 1) +# if defined (TEST_OUTPUT_BUILD_DLL) +# define Test_Output_Export ACE_Proper_Export_Flag +# define TEST_OUTPUT_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# define TEST_OUTPUT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_EXPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# else /* TEST_OUTPUT_BUILD_DLL */ +# define Test_Output_Export ACE_Proper_Import_Flag +# define TEST_OUTPUT_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# define TEST_OUTPUT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_IMPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# endif /* TEST_OUTPUT_BUILD_DLL */ +#else /* TEST_OUTPUT_HAS_DLL == 1 */ +# define Test_Output_Export +# define TEST_OUTPUT_SINGLETON_DECLARATION(T) +# define TEST_OUTPUT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +#endif /* TEST_OUTPUT_HAS_DLL == 1 */ + +// Set TEST_OUTPUT_NTRACE = 0 to turn on library specific tracing even if +// tracing is turned off for ACE. +#if !defined (TEST_OUTPUT_NTRACE) +# if (ACE_NTRACE == 1) +# define TEST_OUTPUT_NTRACE 1 +# else /* (ACE_NTRACE == 1) */ +# define TEST_OUTPUT_NTRACE 0 +# endif /* (ACE_NTRACE == 1) */ +#endif /* !TEST_OUTPUT_NTRACE */ + +#if (TEST_OUTPUT_NTRACE == 1) +# define TEST_OUTPUT_TRACE(X) +#else /* (TEST_OUTPUT_NTRACE == 1) */ +# define TEST_OUTPUT_TRACE(X) ACE_TRACE_IMPL(X) +#endif /* (TEST_OUTPUT_NTRACE == 1) */ + +#endif /* TEST_OUTPUT_EXPORT_H */ + +// End of auto generated file. diff --git a/ACE/tests/Thread_Creation_Threshold_Test.cpp b/ACE/tests/Thread_Creation_Threshold_Test.cpp new file mode 100644 index 00000000000..d18183c7ac4 --- /dev/null +++ b/ACE/tests/Thread_Creation_Threshold_Test.cpp @@ -0,0 +1,239 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Creation_Threshold_Test.cpp +// +// = DESCRIPTION +// This test program stresses how many threads can be +// consecutively (not simultaneously) created on a platform. +// Rather than testing exhaustively, it establishes a +// semi-arbitrary upper limit (MAX_THREAD)of threads. The limit +// is only partly arbitrary because it was chosen as a value that +// exceeded an observed upper limit on the values that Solaris 9 +// will accept as arguments to thr_concurrency(), used by +// ACE_OS::thr_create(THR_NEW_LWP). +// +// = AUTHOR +// Chris Cleeland <cleeland@ociweb.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Task.h" + +ACE_RCSID(tests, Thread_Pool_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) +#include "ace/Lock_Adapter_T.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +// Number of iterations to run the test. +#if defined (ACE_VXWORKS) +const int THREADS_PER_POOL = 50; +const unsigned long MAX_THREAD = 500; +#else +const int THREADS_PER_POOL = 1000; +const unsigned long MAX_THREAD = 100000; +#endif + +namespace +{ + // Change this to 'true' if you want lots of debugging messages in the log + const bool PRINT_DEBUG_MSGS = true; +} + +class Thread_Pool : public ACE_Task<ACE_MT_SYNCH> +{ + // = TITLE + // Defines a thread pool abstraction based on the <ACE_Task>. +public: + Thread_Pool (int n_threads); + // Create the thread pool containing <n_threads>. + + ~Thread_Pool (void); + // Destructor... + + virtual int svc (void); + // Iterate <n_iterations> time printing off a message and "waiting" + // for all other threads to complete this iteration. + + void start(); + // Start the threads in the pool. + + unsigned long total_threads() + { + return this->total_activated_threads_.value(); + } + + int n_threads_; + // Number of threads to spawn. + + bool operator! (); + // Returns true iff failed_ == false. + +private: + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0); + // Spawn the threads in the pool. + + virtual int close (u_long); + // Close hook. + //FUZZ: enable check_for_lack_ACE_OS + + ACE_Atomic_Op<ACE_Thread_Mutex, unsigned long> total_activated_threads_; + // Total number of threads activated through this thread pool ever. + + bool doprint_; + // Flag set only in worker threads to indicate whether they should print + // debug messages. + + bool failed_; + // Flag indicating that start() failed. +}; + +bool +Thread_Pool::operator!() +{ + return ! this->failed_; +} + +Thread_Pool::~Thread_Pool (void) +{ +} + +int +Thread_Pool::close (u_long) +{ + if (this->doprint_) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) worker thread closing down\n"))); + return 0; +} + +Thread_Pool::Thread_Pool (int n_threads) + : n_threads_ (n_threads) + , total_activated_threads_ (0) + , doprint_ (false) + , failed_ (false) +{ +} + +void +Thread_Pool::start () +{ + this->failed_ = false; + if (this->open () == -1) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) thread activation ") + ACE_TEXT ("failed after %u threads\n"), + this->total_threads())); + this->failed_ = true; + } +} + +int +Thread_Pool::svc (void) +{ + unsigned long t = ++this->total_activated_threads_; + if (PRINT_DEBUG_MSGS) // change this to 'true' for debugging + this->doprint_ = (t > 65530); + + if (this->doprint_) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT(" (%P|%t) thread started.\n"))); + + // Note that the <ACE_Task::svc_run> method automatically removes us + // from the <ACE_Thread_Manager> when the thread exits. + return 0; +} + +namespace { + void no_op() { } +} + +int +Thread_Pool::open (void *) +{ + if (PRINT_DEBUG_MSGS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) pool start %d threads...\n"), + this->n_threads_)); + + if (this->total_activated_threads_ >= (65534 - this->n_threads_)) + no_op (); + + // Create a pool of worker threads. + if (this->activate (THR_NEW_LWP, + this->n_threads_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("activate failed")), + -1); + + return 0; +} + +#endif /* ACE_HAS_THREADS */ + + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Creation_Threshold_Test")); + +#if defined (ACE_HAS_THREADS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) will try to start and kill up") + ACE_TEXT (" to %u threads sequentially\n"), + MAX_THREAD)); + int initial_pool_size = 50; + + // Create the worker tasks. + Thread_Pool thread_pool (initial_pool_size); + + while (!thread_pool && thread_pool.total_threads() < MAX_THREAD) + { + // Activate the task's thread pool, produce the messages that are, + // produce the messages that are consumed by the threads in the + // thread pool, and demonstrate how to shutdown by enqueueing + // "empty" messages into the queue. + thread_pool.start (); + + // Wait for all the threads to reach their exit point, at which + // point the barrier in the destructor of the <ACE_Task> portion of + // <Thread_Pool> will return. + if (thread_pool.wait () == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("wait")), 1); + } + + if (PRINT_DEBUG_MSGS) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%u total threads\n"), + thread_pool.total_threads())); + + int& n_threads = thread_pool.n_threads_; + const unsigned long THREAD_THRESHOLD = 63336; + if (thread_pool.total_threads() >= (THREAD_THRESHOLD - n_threads)) + n_threads = 1; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%u total threads successfully started and died;") + ACE_TEXT (" expected %u.\n"), + thread_pool.total_threads (), + MAX_THREAD)); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Thread_Manager_Test.cpp b/ACE/tests/Thread_Manager_Test.cpp new file mode 100644 index 00000000000..b3dc548455b --- /dev/null +++ b/ACE/tests/Thread_Manager_Test.cpp @@ -0,0 +1,472 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Manager_Test.cpp +// +// = DESCRIPTION +// This program tests the group management mechanisms provided by +// the <ACE_Thread_Manager>, including the group signal handling, +// group suspension and resumption, and cooperative thread +// cancellation mechanisms. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Douglas C. Schmidt +// <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread_Manager.h" +#include "ace/Signal.h" +#include "ace/Task.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_time.h" + +ACE_RCSID(tests, Thread_Manager_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) +#include "ace/Barrier.h" + +// Each thread keeps track of whether it has been signalled by using a +// global array. It must be dynamically allocated to allow sizing at +// runtime, based on the number of threads. +static ACE_thread_t *signalled = 0; +static size_t n_threads = ACE_MAX_THREADS; + +// Helper function that looks for an existing entry in the signalled +// array. Also finds the position of the first unused entry in the +// array, and updates if requested with the t_id. +extern "C" int +been_signalled (const ACE_thread_t t_id, + const u_int update = 0) +{ + u_int unused_slot = n_threads; + + for (u_int i = 0; i < n_threads; ++i) + { + if (ACE_OS::thr_equal (signalled[i], t_id)) + // Already signalled. + return 1; + + if (update && + unused_slot == n_threads && + ACE_OS::thr_equal (signalled[i], + ACE_OS::NULL_thread)) + unused_slot = i; + } + + if (update && unused_slot < n_threads) + // Update the array using the first unused_slot. + signalled[unused_slot] = t_id; + + return 0; +} + +// Synchronize starts of threads, so that they all start before the +// main thread cancels them. To avoid creating a static object, it is +// dynamically allocated, before spawning any threads. +static ACE_Barrier *thread_start = 0; + +extern "C" void +handler (int /* signum */) +{ + if (signalled) + { + // No printout here, to be safe. Signal handlers must not + // acquire locks, etc. + const ACE_thread_t t_id = ACE_OS::thr_self (); + + // Update the signalled indication. + (void) been_signalled (t_id, 1u /* update */); + } +} + +static void * +worker (int iterations) +{ +#if 0 + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) %s: stack size is %u\n"), + ACE_OS::thr_self (), + ACE_OS::thr_min_stack ())); +#endif /* ACE_HAS_VXTHREADS */ + +#if !defined (ACE_LACKS_UNIX_SIGNALS) + // Cache this thread's ID. + const ACE_thread_t t_id = ACE_OS::thr_self (); +#endif /* ! ACE_LACKS_UNIX_SIGNAL */ + + ACE_Thread_Manager *thr_mgr = ACE_Thread_Manager::instance (); + + // After setting up the signal catcher, block on the start barrier. + thread_start->wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) worker starting loop\n"))); + + for (int i = 0; i < iterations; i++) + { + if ((i % 1000) == 0) + { +#if !defined (ACE_LACKS_UNIX_SIGNALS) + if (been_signalled (t_id)) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) had received signal\n"))); + + // Only test for cancellation after we've been signaled, + // to avoid race conditions for suspend() and resume(). + if (thr_mgr->testcancel (ACE_Thread::self ()) != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) has been cancelled ") + ACE_TEXT ("before iteration %d!\n"), + i)); + break; + } + } +#else + if (thr_mgr->testcancel (ACE_Thread::self ()) != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) has been cancelled ") + ACE_TEXT ("before iteration %d!\n"), + i)); + break; + } +#endif /* ! ACE_LACKS_UNIX_SIGNAL */ + ACE_OS::sleep (1); + } + } + + // Destructor removes thread from Thread_Manager. + return 0; +} + +static const int DEFAULT_ITERATIONS = 10000; + +// Define a ACE_Task that will serve in the tests related to tasks. + +class ThrMgr_Task : public ACE_Task_Base { +public: + ThrMgr_Task (ACE_Thread_Manager *); + + virtual int svc (void); + + static int errors; +}; + +int ThrMgr_Task::errors = 0; + +// Just be sure to set the ACE_Thread_Manager correctly. +ThrMgr_Task::ThrMgr_Task (ACE_Thread_Manager *mgr) + : ACE_Task_Base (mgr) +{ +} + +// svc just waits til it's been cancelled, then exits. +int +ThrMgr_Task::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Task 0x%x, thread %t waiting to be cancelled\n"), + this)); + ACE_thread_t me = ACE_Thread::self (); + for (int i = 0; i < 30 && !this->thr_mgr ()->testcancel (me); ++i) + ACE_OS::sleep (1); + + if (this->thr_mgr ()->testcancel (me)) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Task 0x%x, thread %t cancelled; exiting\n"), + this)); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Task 0x%x, thread %t was not cancelled\n"), + this)); + ++ThrMgr_Task::errors; + } + return 0; +} + + +// This function tests the task-based record keeping functions. +static int +test_task_record_keeping (ACE_Thread_Manager *mgr) +{ + + int status = 0; + + ThrMgr_Task t1 (mgr), t2 (mgr); + int t1_grp (20), t2_grp (30); + + // Start two tasks, with multiple threads per task. Make sure that + // task_all_list() works. + if (-1 == t1.activate (THR_JOINABLE, 2, 0, + ACE_DEFAULT_THREAD_PRIORITY, t1_grp)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("activate")), 1); + if (-1 == t2.activate (THR_JOINABLE, 3, 0, + ACE_DEFAULT_THREAD_PRIORITY, t2_grp)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("activate")), 1); + + ACE_Task_Base *task_list[10]; + int num_tasks = mgr->task_all_list (task_list, 10); + if (2 != num_tasks) + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Expected 2 tasks; got %d\n"), + num_tasks)); + status = 1; + } + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Got %d tasks - correct\n"), num_tasks)); + if (((task_list[0] == &t1 && task_list[1] == &t2) + || (task_list[1] == &t1 && task_list[0] == &t2)) + && task_list[0] != task_list[1]) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("The Task IDs are correct\n"))); + else + ACE_ERROR ((LM_ERROR, ACE_TEXT ("But Task ID values are wrong!\n"))); + } + ACE_DEBUG ((LM_DEBUG, "Canceling grp %d\n", t1_grp)); + if (-1 == mgr->cancel_grp (t1_grp)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("cancel_grp")), + 1); + ACE_DEBUG ((LM_DEBUG, "Canceling grp %d\n", t2_grp)); + if (-1 == mgr->cancel_grp (t2_grp)) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("cancel_grp")), + 1); + + mgr->wait (); + if (ThrMgr_Task::errors > 0 && status == 0) + status = 1; + + return status; +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Manager_Test")); + int status = 0; + +#if defined (ACE_HAS_THREADS) + size_t n_iterations = DEFAULT_ITERATIONS; + + u_int i; + + // Dynamically allocate signalled so that we can control when it + // gets deleted. Specifically, we need to delete it before the main + // thread's TSS is cleaned up. + ACE_NEW_RETURN (signalled, + ACE_thread_t[n_threads], + 1); + // Initialize each ACE_thread_t to avoid Purify UMR's. + for (i = 0; i < n_threads; ++i) + signalled[i] = ACE_OS::NULL_thread; + + // And similarly, dynamically allocate the thread_start barrier. + ACE_NEW_RETURN (thread_start, + ACE_Barrier (n_threads + 1), + -1); + + // Register a signal handler. + ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); + ACE_UNUSED_ARG (sa); + + ACE_Thread_Manager *thr_mgr = ACE_Thread_Manager::instance (); + +#if 0 + // Assign thread (VxWorks task) names to test that feature. + ACE_hthread_t *thread_name = 0; + ACE_NEW_RETURN (thread_name, + ACE_hthread_t[n_threads], + -1); + + // And test the ability to specify stack size. + size_t *stack_size = 0; + ACE_NEW_RETURN (stack_size, + size_t[n_threads], + -1); + + for (i = 0; i < n_threads; ++i) + { + if (i < n_threads - 1) + { + ACE_NEW_RETURN (thread_name[i], + char[32], + -1); + ACE_OS::sprintf (thread_name[i], + ACE_TEXT ("thread%u"), + i); + } + else + // Pass an ACE_thread_t pointer of 0 for the last thread name. + thread_name[n_threads - 1] = 0; + + stack_size[i] = 40000; + } +#endif /* ACE_HAS_VXTHREADS */ + + int grp_id = thr_mgr->spawn_n + ( + n_threads, + (ACE_THR_FUNC) worker, + reinterpret_cast <void *> (n_iterations), + THR_BOUND + , ACE_DEFAULT_THREAD_PRIORITY + , -1 +#if 0 + , 0 + , thread_name + , 0 + , stack_size +#endif /* ACE_HAS_VXTHREADS */ + ); + + ACE_ASSERT (grp_id != -1); + thread_start->wait (); + + // Wait for 1 second and then suspend every thread in the group. + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) suspending group\n"))); + if (thr_mgr->suspend_grp (grp_id) == -1) + { + // Pthreads w/o UNIX 98 extensions doesn't support suspend/resume, + // so it's allowed to ENOTSUP as long as the config indicates this. + if (errno == ENOTSUP) + { +#if defined (ACE_HAS_PTHREADS) && (defined (ACE_HAS_PTHREAD_SUSPEND) || \ + defined (ACE_HAS_PTHREAD_SUSPEND_NP)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("suspend_grp: ENOTSUP but config ") + ACE_TEXT ("says it should work.\n"))); +#else + ACE_DEBUG((LM_DEBUG, + ACE_TEXT (" OK: suspend_grp isn't supported with ") + ACE_TEXT ("Pthreads\n"))); +#endif /* ACE_HAS_PTHREADS && should be able to suspend */ + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("suspend_grp"))); + } + } + + // Wait for 1 more second and then resume every thread in the + // group. + ACE_OS::sleep (ACE_Time_Value (1)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) resuming group\n"))); + + if (thr_mgr->resume_grp (grp_id) == -1) + { + // Pthreads w/o UNIX 98 extensions doesn't support suspend/resume, + // so it's allowed to ENOTSUP as long as the config indicates this. + if (errno == ENOTSUP) + { +#if defined (ACE_HAS_PTHREADS) && (defined (ACE_HAS_PTHREAD_CONTINUE) || \ + defined (ACE_HAS_PTHREAD_CONTINUE_NP) || \ + defined (ACE_HAS_PTHREAD_RESUME_NP)) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("resume_grp: ENOTSUP but config ") + ACE_TEXT ("says it should work.\n"))); +#else + ACE_DEBUG((LM_DEBUG, + ACE_TEXT (" OK: resume_grp isn't supported with ") + ACE_TEXT ("Pthreads\n"))); +#endif /* ACE_HAS_PTHREADS && should be able to continue/resume */ + } + else + { + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("resume_grp"))); + } + } + + // Wait for 1 more second and then send a SIGINT to every thread in + // the group. + ACE_OS::sleep (ACE_Time_Value (1)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) signaling group\n"))); + +#if defined (ACE_HAS_WTHREADS) + thr_mgr->kill_grp (grp_id, + SIGINT); +#elif !defined (ACE_HAS_PTHREADS_DRAFT4) && !defined(ACE_LACKS_PTHREAD_KILL) +#if defined (CHORUS) + ACE_ASSERT (thr_mgr->kill_grp (grp_id, + SIGTHREADKILL) != -1); +#else + ACE_ASSERT (thr_mgr->kill_grp (grp_id, + SIGINT) != -1); +#endif /* CHORUS */ +#else + if (thr_mgr->kill_grp (grp_id, + SIGINT) == -1) + ACE_ASSERT (errno == ENOTSUP); +#endif /* ACE_HAS_WTHREADS */ + + // Wait and then cancel all the threads. + ACE_OS::sleep (ACE_Time_Value (1)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) cancelling group\n"))); + + ACE_ASSERT (thr_mgr->cancel_grp (grp_id) != -1); + + // Perform a barrier wait until all the threads have shut down. + // But, wait for a limited time, just in case. + ACE_Time_Value const max_wait (600); + ACE_Time_Value const wait_time (ACE_OS::gettimeofday () + max_wait); + if (thr_mgr->wait (&wait_time) == -1) + { + if (errno == ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("maximum wait time of %d msec exceeded\n"), + max_wait.msec ())); + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), ACE_TEXT ("wait"))); + status = -1; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) main thread finished\n"))); + +#if 0 + for (i = 0; i < n_threads - 1; ++i) + { + delete [] thread_name[i]; + // Don't delete the last thread_name, because it points to the + // name in the TCB. It was initially 0. + } + delete [] thread_name; + delete [] stack_size; +#endif /* ACE_HAS_VXTHREADS */ + + delete thread_start; + thread_start = 0; + delete [] signalled; + signalled = 0; + + // Now test task record keeping + if (test_task_record_keeping (thr_mgr) != 0) + status = -1; + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return status; +} diff --git a/ACE/tests/Thread_Mutex_Test.cpp b/ACE/tests/Thread_Mutex_Test.cpp new file mode 100644 index 00000000000..048aa855a6a --- /dev/null +++ b/ACE/tests/Thread_Mutex_Test.cpp @@ -0,0 +1,276 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Mutex_Test.cpp +// +// = DESCRIPTION +// This test illustrates the functionality of the +// ACE_Thread_Mutex. The test acquires and releases mutexes. No +// command line arguments are needed to run the test. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and Douglas C. Schmidt <schmidt@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread_Manager.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Thread_Mutex_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#include "ace/Guard_T.h" + +// For all platforms except for Windows use the ACE_Thread_Mutex. +// Since Windows only supports timed process mutexes and not +// timed thread mutexes, use ACE_Process_Mutex. +#if defined (ACE_HAS_WTHREADS) +# include "ace/Process_Mutex.h" + typedef ACE_Process_Mutex ACE_TEST_MUTEX; +#else +# include "ace/Thread_Mutex.h" + typedef ACE_Thread_Mutex ACE_TEST_MUTEX; +#endif + +#if !defined (ACE_HAS_MUTEX_TIMEOUTS) +static int reported_notsup = 0; +#endif /* ACE_HAS_MUTEX_TIMEOUTS */ + +static void * +test (void *args) +{ + ACE_TEST_MUTEX *mutex = (ACE_TEST_MUTEX *) args; + ACE_UNUSED_ARG (mutex); // Suppress ghs warning about unused local "mutex". + ACE_OS::srand ((u_int) ACE_OS::time (0)); + + for (size_t i = 0; i < ACE_MAX_ITERATIONS / 2; i++) + { + int result = 0; + + // First attempt to acquire the mutex with a timeout to verify + // that mutex timeouts are working. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = trying timed acquire on ") + ACE_TEXT ("iteration %d\n"), + i)); + + ACE_Time_Value delta (1, 0); // One second timeout + ACE_Time_Value timeout = ACE_OS::gettimeofday (); + timeout += delta; // Must pass absolute time to acquire(). + + if (mutex->acquire (timeout) != 0) + { + if (errno == ETIME) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = mutex acquisition ") + ACE_TEXT ("timed out\n"))); + else if (errno == ENOTSUP) + { +#if !defined (ACE_HAS_MUTEX_TIMEOUTS) + if (!reported_notsup) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("(%P|%t) %p, but ACE_HAS_MUTEX_TIMEOUTS is not defined - Ok\n"), + ACE_TEXT ("mutex timed acquire"))); + reported_notsup = 1; + } +#else + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p - maybe ACE_HAS_MUTEX_TIMEOUTS should not be defined?\n"), + ACE_TEXT ("mutex timed acquire"))); +#endif /* ACE_HAS_MUTEX_TIMEOUTS */ + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n%a"), + ACE_TEXT ("mutex timeout failed\n"))); + return 0; + } + } + else + { + result = mutex->release (); + ACE_ASSERT (result == 0); + } + + // Now try the standard mutex. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = trying to acquire on iteration %d\n"), + i)); + result = mutex->acquire (); + ACE_ASSERT (result == 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = acquired on iteration %d\n"), + i)); + + // Sleep for a random amount of time between 0 and 2 seconds. + // Note that it's ok to use rand() here because we are running + // within the critical section defined by the Thread_Mutex. + ACE_OS::sleep (ACE_OS::rand () % 2); + + result = mutex->release (); + ACE_ASSERT (result == 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) = released on iteration %d\n"), + i)); + + // Basic ACE_Guard usage - automatically acquire the mutex on + // guard construction and automatically release it on + // destruction. + { + // Construct an ACE_Guard to implicitly acquire the mutex. + ACE_Guard<ACE_TEST_MUTEX> guard (*mutex); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // ACE_Guard object is destroyed when exiting scope and guard + // destructor automatically releases mutex. + } + + // Use an ACE_Guard to automatically acquire a mutex, but release + // the mutex early. + { + // Construct an ACE_Guard to implicitly acquire the mutex. + ACE_Guard<ACE_TEST_MUTEX> guard (*mutex); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // Release the mutex since we no longer need it. + guard.release (); + ACE_ASSERT (guard.locked () == 0); + + // Do something else which does not require the mutex to be locked. + // ... + + // ACE_Guard object's destructor will not release the mutex. + } + + // Use an ACE_Guard to automatically acquire a mutex, but + // relinquish ownership of the lock so that the mutex is not + // automatically released on guard destruction. This is useful + // when an operation might not release the mutex in some + // conditions, in which case responsibility for releasing it is + // passed to someone else. + { + // Construct an ACE_Guard to implicitly acquire the mutex. + ACE_Guard<ACE_TEST_MUTEX> guard (*mutex); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // Relinquish ownership of the mutex lock. Someone else must + // now release it. + guard.disown (); + ACE_ASSERT (guard.locked () == 0); + + // ACE_Guard object's destructor will not release the mutex. + } + // We are now responsible for releasing the mutex. + result = mutex->release (); + ACE_ASSERT (result == 0); + + // Construct an ACE_Guard without automatically acquiring the lock. + { + // Construct an ACE_Guard object without automatically + // acquiring the mutex or taking ownership of an existing + // lock. The third parameter tells the guard that the mutex + // has not been locked. + ACE_Guard<ACE_TEST_MUTEX> guard (*mutex, 0, 0); + ACE_ASSERT (guard.locked () == 0); + + // Conditionally acquire the mutex. + if (i % 2 == 0) + { + guard.acquire (); + ACE_ASSERT (guard.locked () != 0); + } + + // Perform some operation that might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // ACE_Guard object is destroyed when exiting scope and guard + // destructor automatically releases if it was acquired above. + } + + // Use an ACE_Guard to take ownership of a previously acquired + // mutex. + timeout = ACE_OS::gettimeofday (); + timeout += delta; // Must pass absolute time to acquire(). + if (mutex->acquire (timeout) == 0) + { + // Construct an ACE_Guard object without automatically + // acquiring the mutex, but instead take ownership of the + // existing lock. The third parameter tells the guard that + // the mutex has already been locked. + ACE_Guard<ACE_TEST_MUTEX> guard (*mutex, 0, 1); + ACE_ASSERT (guard.locked () != 0); + + // Perform some operation which might exit the current scope + // prematurely, e.g. by returning or throwing an exception. + // ... + + // ACE_Guard object is destroyed when exiting scope and guard + // destructor automatically releases mutex. + } + } + + return 0; +} +#endif /* ACE_HAS_THREADS */ + +static void +spawn (void) +{ +#if defined (ACE_HAS_THREADS) + ACE_TEST_MUTEX mutex; + + const u_int n_threads = ACE_MAX_THREADS; + + if (ACE_Thread_Manager::instance ()->spawn_n (n_threads, + ACE_THR_FUNC (test), + (void *) &mutex, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n%a"), + ACE_TEXT ("thread create failed"))); + + // Wait for the threads to exit. + ACE_Thread_Manager::instance ()->wait (); + +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Mutex_Test")); + + spawn (); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Thread_Pool_Reactor_Resume_Test.cpp b/ACE/tests/Thread_Pool_Reactor_Resume_Test.cpp new file mode 100644 index 00000000000..59de935d0f7 --- /dev/null +++ b/ACE/tests/Thread_Pool_Reactor_Resume_Test.cpp @@ -0,0 +1,393 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Pool_Reactor_Resume_Test.cpp +// +// = DESCRIPTION +// This program is an additional torture test of thread pool +// reactors. This test is based on Thread_Pool_Reactor_Test.cpp +// in $ACE_ROOT/tests. This test differs from the other one +// slightly. The TP reactor is instantiated with the +// with a value of 1 for the <resume_flag> argument. The server +// threads during the handle_input call resumes the handle that +// would have been suspended by the reactor. +// +// Usage: Thread_Pool_Reactor_Test [-r <hostname:port#>] +// [-s <server thr#>] [-c <client thr#>] [-d <delay>] +// [-i <client conn attempt#>] [-n <client request# per conn>] +// +// Default value: +// <hostname:port#>: ACE_DEFAULT_RENDEZVOUS +// <server thr#>: ACE_MAX_THREADS +// <client thr#>: ACE_MAX_ITERATIONS +// <client conn attempt#>: ACE_MAX_ITERATIONS +// <client req# per conn>: ACE_MAX_THREADS +// <delay>: 50 usec +// +// = AUTHOR +// Balachandran Natarajan <bala@cs.wustl.edu> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Get_Opt.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Acceptor.h" +#include "ace/Thread_Manager.h" +#include "ace/TP_Reactor.h" + +ACE_RCSID(tests, Atomic_Op_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#include "tests/Thread_Pool_Reactor_Resume_Test.h" +typedef ACE_Strategy_Acceptor <Request_Handler, ACE_SOCK_ACCEPTOR> ACCEPTOR; + +// Accepting end point. This is actually "localhost:10010", but some +// platform couldn't resolve the name so we use the IP address +// directly here. +static const ACE_TCHAR *rendezvous = ACE_TEXT ("127.0.0.1:10010"); + +// Total number of server threads. +static size_t svr_thrno = ACE_MAX_THREADS; + + +#if defined (CHORUS) \ + || defined (ACE_VXWORKS) // default network parameters (MAX_BINDS and system buffers) are too small for full test + // Add platforms that can't handle too many + // connection simultaneously here. +#define ACE_LOAD_FACTOR /2 +#else +#define ACE_LOAD_FACTOR +#endif + +// Total number of client threads. +static size_t cli_thrno = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Total connection attemps of a client thread. +static size_t cli_conn_no = ACE_MAX_ITERATIONS ACE_LOAD_FACTOR; + +// Total requests a client thread sends. +static size_t cli_req_no = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Delay before a thread sending the next request (in msec.) +static int req_delay = 50; + +static void +parse_arg (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("r:s:c:d:i:n:")); + + int c; + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 'r': // hostname:port + rendezvous = getopt.opt_arg (); + break; + case 's': + svr_thrno = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'c': + cli_thrno = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'd': + req_delay = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'i': + cli_conn_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'n': + cli_req_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, + "Usage: Thread_Pool_Reactor_Resume_Test [-r <hostname:port#>]" + "\t[-s <server thr#>] [-c <client thr#>] [-d <delay>]" + "\t[-i <client conn attempt#>]" + "[-n <client request# per conn>]\n")); + break; + } + } +} + +Request_Handler::Request_Handler (ACE_Thread_Manager *thr_mgr) + : ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH> (thr_mgr), + nr_msgs_rcvd_(0) +{ + // Enable reference counting. + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + // Make sure we use TP_Reactor with this class (that's the whole + // point, right?) + this->reactor (ACE_Reactor::instance ()); +} + +int +Request_Handler::open (void *arg) +{ + // Open base class. + int result = + ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH>::open (arg); + + // Return on error. + if (result == -1) + return -1; + + // Else we have successfully registered with the Reactor. Give our + // ownership to the Reactor. + this->remove_reference (); + + // Return result. + return result; +} + +Request_Handler::~Request_Handler (void) +{ +} + +int +Request_Handler::resume_handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) resume_handler () called \n"))); + return 1; +} + +int +Request_Handler::handle_input (ACE_HANDLE fd) +{ + ACE_TCHAR buffer[BUFSIZ]; + ACE_TCHAR len = 0; + ssize_t result = this->peer ().recv (&len, sizeof (ACE_TCHAR)); + + if (result > 0 + && this->peer ().recv_n (buffer, len * sizeof (ACE_TCHAR)) + == static_cast<ssize_t> (len * sizeof (ACE_TCHAR))) + { + ++this->nr_msgs_rcvd_; + + // Now the handle_input method has done what it can do, namely + // read the data from the socket we can just resume the handler + // at this point + ACE_DEBUG ((LM_DEBUG, + "(%t) svr input; fd: 0x%x; input: %s\n", + fd, + buffer)); + if (ACE_OS::strcmp (buffer, ACE_TEXT ("shutdown")) == 0) + ACE_Reactor::instance()->end_reactor_event_loop (); + + this->reactor ()->resume_handler (fd); + return 0; + } + else + { + ACE_DEBUG ((LM_DEBUG, + "(%t) Errno is %d and result is %d\n", + errno, result)); + ACE_DEBUG ((LM_DEBUG, + "(%t) Request_Handler: 0x%x peer closed (0x%x)\n", + this, fd)); + } + return -1; +} + +int +Request_Handler::handle_close (ACE_HANDLE fd, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) svr close; fd: 0x%x, rcvd %d msgs\n", + fd, + this->nr_msgs_rcvd_)); + if (this->nr_msgs_rcvd_ != cli_req_no) + ACE_ERROR((LM_ERROR, + "(%t) Handler 0x%x: Expected %d messages; got %d\n", + this, + cli_req_no, + this->nr_msgs_rcvd_)); + + return 0; +} + +static int +reactor_event_hook (ACE_Reactor *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) handling events ....\n")); + + return 0; +} + +static ACE_THR_FUNC_RETURN +svr_worker (void *) +{ + // Server thread function. + int result = + ACE_Reactor::instance ()->run_reactor_event_loop (&reactor_event_hook); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) %p\n", + "Error handling events"), + 0); + + ACE_DEBUG ((LM_DEBUG, + "(%t) I am done handling events. Bye, bye\n")); + + return 0; +} + +static ACE_THR_FUNC_RETURN +cli_worker (void *arg) +{ + // Client thread function. + ACE_INET_Addr addr (rendezvous); + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connect; + ACE_Time_Value delay (0, req_delay); + size_t len = * reinterpret_cast<ACE_TCHAR *> (arg); + + for (size_t i = 0 ; i < cli_conn_no; i++) + { + if (connect.connect (stream, addr) < 0) + { + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "connect")); + continue; + } + + for (size_t j = 0; j < cli_req_no; j++) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) conn_worker handle = %x, req = %d\n", + stream.get_handle (), + j+1)); + if (stream.send_n (arg, + (len + 1) * sizeof (ACE_TCHAR)) == -1) + { + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "send_n")); + continue; + } + ACE_OS::sleep (delay); + } + + stream.close (); + } + + return 0; +} + +static ACE_THR_FUNC_RETURN +worker (void *) +{ + ACE_OS::sleep (3); + const ACE_TCHAR *msg = ACE_TEXT ("Message from Connection worker"); + ACE_TCHAR buf [BUFSIZ]; + buf[0] = static_cast<ACE_TCHAR> ((ACE_OS::strlen (msg) + 1)); + ACE_OS::strcpy (&buf[1], msg); + + ACE_INET_Addr addr (rendezvous); + + ACE_DEBUG((LM_DEBUG, + "(%t) Spawning %d client threads...\n", + cli_thrno)); + int grp = ACE_Thread_Manager::instance ()->spawn_n (cli_thrno, + &cli_worker, + buf); + ACE_ASSERT (grp != -1); + + ACE_Thread_Manager::instance ()->wait_grp (grp); + + ACE_DEBUG ((LM_DEBUG, + "(%t) Client threads done; shutting down...\n")); + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connect; + + if (connect.connect (stream, addr) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p Error while connecting\n", + "connect")); + + const ACE_TCHAR *sbuf = ACE_TEXT ("\011shutdown"); + + ACE_DEBUG ((LM_DEBUG, + "shutdown stream handle = %x\n", + stream.get_handle ())); + + if (stream.send_n (sbuf, (ACE_OS::strlen (sbuf) + 1) * sizeof (ACE_TCHAR)) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "send_n")); + + ACE_DEBUG ((LM_DEBUG, + "Sent message of length = %d\n", + ACE_OS::strlen (sbuf))); + stream.close (); + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Reactor_Resume_Test")); + parse_arg (argc, argv); + + // Changed the default + ACE_TP_Reactor sr; + + + ACE_Reactor new_reactor (&sr); + ACE_Reactor::instance (&new_reactor); + + ACCEPTOR acceptor; + ACE_INET_Addr accept_addr (rendezvous); + + if (acceptor.open (accept_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 1); + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("(%t) Spawning %d server threads...\n"), + svr_thrno)); + ACE_Thread_Manager::instance ()->spawn_n (svr_thrno, + svr_worker); + ACE_Thread_Manager::instance ()->spawn (worker); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_END_TEST; + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Reactor_Test")); + + ACE_ERROR ((LM_INFO, + "threads not supported on this platform\n")); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/Thread_Pool_Reactor_Resume_Test.h b/ACE/tests/Thread_Pool_Reactor_Resume_Test.h new file mode 100644 index 00000000000..f3f18a5c712 --- /dev/null +++ b/ACE/tests/Thread_Pool_Reactor_Resume_Test.h @@ -0,0 +1,56 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Pool_Reactor_Test.h +// +// = DESCRIPTION +// This class gets its own header file to work around AIX C++ +// compiler "features" related to template instantiation... It is +// only used by Thread_Pool_Reactor_Test.cpp. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_THREAD_POOL_REACTOR_RESUME_TEST_H +#define ACE_TESTS_THREAD_POOL_REACTOR_RESUME_TEST_H + +#include "ace/SOCK_Stream.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Svc_Handler.h" + +class Request_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH> +{ + // = TITLE + // This class is the Svc_Handler used by <Acceptor>. +public: + + /// The default constructor makes sure the right reactor is used. + Request_Handler (ACE_Thread_Manager *tm = 0); + + /// Dtor.. + ~Request_Handler (void); + + virtual int open (void * = 0); + +protected: + virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE); + virtual int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask = 0); + virtual int resume_handler (void); + +private: + size_t nr_msgs_rcvd_; +}; + +#endif /* ACE_TESTS_THREAD_POOL_REACTOR_RESUME_TEST_H */ diff --git a/ACE/tests/Thread_Pool_Reactor_Test.cpp b/ACE/tests/Thread_Pool_Reactor_Test.cpp new file mode 100644 index 00000000000..e60233777e7 --- /dev/null +++ b/ACE/tests/Thread_Pool_Reactor_Test.cpp @@ -0,0 +1,345 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Pool_Reactor_Test.cpp +// +// = DESCRIPTION +// This program is a torture test of thread pool reactors. It +// starts by spawning several server threads waiting to handle +// events. Several other client threads are spawned right after +// to initiate connections to server threads. Each connection +// adds a new Svc_Handler into the TP_Reactor and sends out +// several "requests" to the server thread. After the connection +// is closed, the Svc_Handler is removed from the TP_Reactor. +// Each message is treated as a separate request by the server so +// two consecutive requests might be serviced by two different +// threads. +// +// Usage: Thread_Pool_Reactor_Test [-r <hostname:port#>] +// [-s <server thr#>] [-c <client thr#>] [-d <delay>] +// [-i <client conn attempt#>] [-n <client request# per conn>] +// +// Default value: +// <hostname:port#>: ACE_DEFAULT_RENDEZVOUS +// <server thr#>: ACE_MAX_THREADS +// <client thr#>: ACE_MAX_ITERATIONS +// <client conn attempt#>: ACE_MAX_ITERATIONS +// <client req# per conn>: ACE_MAX_THREADS +// <delay>: 50 usec +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> and +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Get_Opt.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Acceptor.h" +#include "ace/Thread_Manager.h" +#include "ace/TP_Reactor.h" + +ACE_RCSID(tests, Atomic_Op_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#include "tests/Thread_Pool_Reactor_Test.h" +typedef ACE_Strategy_Acceptor <Request_Handler, ACE_SOCK_ACCEPTOR> ACCEPTOR; + +// Accepting end point. This is actually "localhost:10010", but some +// platform couldn't resolve the name so we use the IP address +// directly here. +static const ACE_TCHAR *rendezvous = ACE_TEXT ("127.0.0.1:10010"); + +// Total number of server threads. +static size_t svr_thrno = ACE_MAX_THREADS; + +#if defined (ACE_VXWORKS) // default network parameters (MAX_BINDS and system buffers) are too small for full test + // Add platforms that can't handle too many + // connection simultaneously here. +#define ACE_LOAD_FACTOR /2 +#else +#define ACE_LOAD_FACTOR +#endif + +// Total number of client threads. +static size_t cli_thrno = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Total connection attemps of a client thread. +static size_t cli_conn_no = ACE_MAX_ITERATIONS ACE_LOAD_FACTOR; + +// Total requests a client thread sends. +static size_t cli_req_no = ACE_MAX_THREADS ACE_LOAD_FACTOR; + +// Delay before a thread sending the next request (in msec.) +static int req_delay = 50; + +static void +parse_arg (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("r:s:c:d:i:n:")); + + int c; + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 'r': // hostname:port + rendezvous = getopt.opt_arg (); + break; + case 's': + svr_thrno = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'c': + cli_thrno = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'd': + req_delay = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'i': + cli_conn_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'n': + cli_req_no = ACE_OS::atoi (getopt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, + "Usage: Thread_Pool_Reactor_Test [-r <hostname:port#>]" + "\t[-s <server thr#>] [-c <client thr#>] [-d <delay>]" + "\t[-i <client conn attempt#>]" + "[-n <client request# per conn>]\n")); + break; + } + } +} + +Request_Handler::Request_Handler (ACE_Thread_Manager *thr_mgr) + : ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH> (thr_mgr), + nr_msgs_rcvd_(0) +{ + // Make sure we use TP_Reactor with this class (that's the whole + // point, right?) + this->reactor (ACE_Reactor::instance ()); +} + +int +Request_Handler::handle_input (ACE_HANDLE fd) +{ + ACE_TCHAR buffer[BUFSIZ]; + ACE_TCHAR len = 0; + ssize_t result = this->peer ().recv (&len, sizeof (ACE_TCHAR)); + + if (result > 0 + && this->peer ().recv_n (buffer, len * sizeof (ACE_TCHAR)) + == static_cast<ssize_t> (len * sizeof (ACE_TCHAR))) + { + ++this->nr_msgs_rcvd_; + + ACE_DEBUG ((LM_DEBUG, + "(%t) svr input; fd: 0x%x; input: %s\n", + fd, + buffer)); + if (ACE_OS::strcmp (buffer, ACE_TEXT ("shutdown")) == 0) + ACE_Reactor::instance()->end_reactor_event_loop (); + return 0; + } + else + ACE_DEBUG ((LM_DEBUG, + "(%t) Request_Handler: 0x%x peer closed (0x%x)\n", + this, fd)); + return -1; +} + +int +Request_Handler::handle_close (ACE_HANDLE fd, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) svr close; fd: 0x%x, rcvd %d msgs\n", + fd, + this->nr_msgs_rcvd_)); + if (this->nr_msgs_rcvd_ != cli_req_no) + ACE_ERROR((LM_ERROR, + "(%t) Handler 0x%x: Expected %d messages; got %d\n", + this, + cli_req_no, + this->nr_msgs_rcvd_)); + this->destroy (); + return 0; +} + +static int +reactor_event_hook (ACE_Reactor *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) handling events ....\n")); + + return 0; +} + +static ACE_THR_FUNC_RETURN +svr_worker (void *) +{ + // Server thread function. + int result = + ACE_Reactor::instance ()->run_reactor_event_loop (&reactor_event_hook); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) %p\n", + "Error handling events"), + 0); + + ACE_DEBUG ((LM_DEBUG, + "(%t) I am done handling events. Bye, bye\n")); + + return 0; +} + +static ACE_THR_FUNC_RETURN +cli_worker (void *arg) +{ + // Client thread function. + ACE_INET_Addr addr (rendezvous); + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connect; + ACE_Time_Value delay (0, req_delay); + size_t len = * reinterpret_cast<ACE_TCHAR *> (arg); + + for (size_t i = 0 ; i < cli_conn_no; i++) + { + if (connect.connect (stream, addr) < 0) + { + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "connect")); + continue; + } + + for (size_t j = 0; j < cli_req_no; j++) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) conn_worker handle 0x%x, req %d\n", + stream.get_handle (), + j+1)); + if (stream.send_n (arg, + (len + 1) * sizeof (ACE_TCHAR)) == -1) + { + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "send_n")); + continue; + } + ACE_OS::sleep (delay); + } + + stream.close (); + } + + return 0; +} + +static ACE_THR_FUNC_RETURN +worker (void *) +{ + ACE_OS::sleep (3); + const ACE_TCHAR *msg = ACE_TEXT ("Message from Connection worker"); + ACE_TCHAR buf [BUFSIZ]; + buf[0] = static_cast<ACE_TCHAR> ((ACE_OS::strlen (msg) + 1)); + ACE_OS::strcpy (&buf[1], msg); + + ACE_INET_Addr addr (rendezvous); + + ACE_DEBUG((LM_DEBUG, + "(%t) Spawning %d client threads...\n", + cli_thrno)); + int grp = ACE_Thread_Manager::instance ()->spawn_n (cli_thrno, + &cli_worker, + buf); + ACE_ASSERT (grp != -1); + + ACE_Thread_Manager::instance ()->wait_grp (grp); + + ACE_DEBUG ((LM_DEBUG, + "(%t) Client threads done; shutting down...\n")); + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connect; + + if (connect.connect (stream, addr) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p Error while connecting\n", + "connect")); + + const ACE_TCHAR *sbuf = ACE_TEXT ("\011shutdown"); + + ACE_DEBUG ((LM_DEBUG, + "shutdown stream handle = %x\n", + stream.get_handle ())); + + if (stream.send_n (sbuf, (ACE_OS::strlen (sbuf) + 1) * sizeof (ACE_TCHAR)) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "send_n")); + + stream.close (); + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Reactor_Test")); + parse_arg (argc, argv); + + // Changed the default + ACE_TP_Reactor sr; + ACE_Reactor new_reactor (&sr); + ACE_Reactor::instance (&new_reactor); + + ACCEPTOR acceptor; + ACE_INET_Addr accept_addr (rendezvous); + + if (acceptor.open (accept_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 1); + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("(%t) Spawning %d server threads...\n"), + svr_thrno)); + ACE_Thread_Manager::instance ()->spawn_n (svr_thrno, + svr_worker); + ACE_Thread_Manager::instance ()->spawn (worker); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_END_TEST; + return 0; +} + +#else +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Reactor_Test")); + + ACE_ERROR ((LM_INFO, + "threads not supported on this platform\n")); + + ACE_END_TEST; + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/Thread_Pool_Reactor_Test.h b/ACE/tests/Thread_Pool_Reactor_Test.h new file mode 100644 index 00000000000..0dfeab64528 --- /dev/null +++ b/ACE/tests/Thread_Pool_Reactor_Test.h @@ -0,0 +1,49 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Pool_Reactor_Test.h +// +// = DESCRIPTION +// This class gets its own header file to work around AIX C++ +// compiler "features" related to template instantiation... It is +// only used by Thread_Pool_Reactor_Test.cpp. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// Nanbor Wang <nanbor@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_THREAD_POOL_REACTOR_TEST_H +#define ACE_TESTS_THREAD_POOL_REACTOR_TEST_H + +#include "ace/SOCK_Stream.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Svc_Handler.h" + +class Request_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH> +{ + // = TITLE + // This class is the Svc_Handler used by <Acceptor>. +public: + Request_Handler (ACE_Thread_Manager *tm = 0); + // The default constructor makes sure the right reactor is used. + +protected: + virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE); + virtual int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask = 0); + +private: + size_t nr_msgs_rcvd_; +}; + +#endif /* ACE_TESTS_THREAD_POOL_REACTOR_TEST_H */ diff --git a/ACE/tests/Thread_Pool_Test.cpp b/ACE/tests/Thread_Pool_Test.cpp new file mode 100644 index 00000000000..6c124e494cd --- /dev/null +++ b/ACE/tests/Thread_Pool_Test.cpp @@ -0,0 +1,461 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Thread_Pool_Test.cpp +// +// = DESCRIPTION +// This test program illustrates how the <ACE_Task> +// synchronization mechanisms work in conjunction with the +// <ACE_Thread_Manager>. If the <manual> flag is set input comes +// from stdin until the user enters a return -- otherwise, the +// input is generated automatically. All worker threads shutdown +// when (1) they receive a message block of length 0 or (2) the +// queue is deactivated. +// +// = AUTHOR +// Karlheinz Dorn <Karlheinz.Dorn@med.siemens.de>, +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, and +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Task.h" + +ACE_RCSID(tests, Thread_Pool_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) +#include "ace/Lock_Adapter_T.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +// Number of iterations to run the test. +static size_t n_iterations = 100; + +// Controls whether the input is generated "manually" or automatically. +static int manual = 0; + +class Thread_Pool : public ACE_Task<ACE_MT_SYNCH> +{ + // = TITLE + // Defines a thread pool abstraction based on the <ACE_Task>. +public: + Thread_Pool (int n_threads); + // Create the thread pool containing <n_threads>. + + ~Thread_Pool (void); + // Destructor... + + int test_queue_deactivation_shutdown (void); + // Activate the task's thread pool, produce the messages that are + // consumed by the threads in the thread pool, and demonstate how to + // shutdown using the <ACE_Message_Queue::deactivate> method. + + int test_empty_message_shutdown (void); + // Activate the task's thread pool, produce the messages that are, + // produce the messages that are consumed by the threads in the + // thread pool, and demonstrate how to shutdown by enqueueing + // "empty" messages into the queue. + + virtual int svc (void); + // Iterate <n_iterations> time printing off a message and "waiting" + // for all other threads to complete this iteration. + + virtual int put (ACE_Message_Block *mb, + ACE_Time_Value *tv = 0); + // Allows the producer to pass messages to the <Thread_Pool>. + +private: + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void * = 0); + // Spawn the threads in the pool. + + virtual int close (u_long); + // Close hook. + //FUZZ: enable check_for_lack_ACE_OS + + ACE_Lock_Adapter<ACE_Thread_Mutex> lock_adapter_; + // Serialize access to <ACE_Message_Block> reference count, which + // will be decremented by multiple threads. + + int n_threads_; + // Number of threads to spawn. +}; + +Thread_Pool::~Thread_Pool (void) +{ +} + +int +Thread_Pool::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) worker thread closing down\n"))); + return 0; +} + +Thread_Pool::Thread_Pool (int n_threads) + : n_threads_ (n_threads) +{ +} + +// Simply enqueue the Message_Block into the end of the queue. + +int +Thread_Pool::put (ACE_Message_Block *mb, + ACE_Time_Value *tv) +{ + return this->putq (mb, tv); +} + +// Iterate <n_iterations> printing off a message and "waiting" for all +// other threads to complete this iteration. + +int +Thread_Pool::svc (void) +{ + // Keep looping, reading a message out of the queue, until we get a + // message with a length == 0, which signals us to quit. + + for (int count = 1; ; count++) + { + ACE_Message_Block *mb = 0; + + int result = this->getq (mb); + + ACE_ASSERT (result != -1 || errno == ESHUTDOWN); + + if (result == -1 && errno == ESHUTDOWN) + { + // The queue has been deactivated, so let's bail out. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) in iteration %d, queue len = %d, ") + ACE_TEXT ("queue deactivated, exiting\n"), + count, + this->msg_queue ()->message_count ())); + + break; + } + + size_t length = mb->length (); + + if (length > 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) in iteration %d, queue len = %d, ") + ACE_TEXT ("length = %d, text = \"%*s\"\n"), + count, + this->msg_queue ()->message_count (), + length, + length - 1, + mb->rd_ptr ())); + + // We're responsible for deallocating this. + mb->release (); + + if (length == 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) in iteration %d, queue len = %d, ") + ACE_TEXT ("got \"empty\" message, exiting\n"), + count, + this->msg_queue ()->message_count ())); + break; + } + } + + // Note that the <ACE_Task::svc_run> method automatically removes us + // from the <ACE_Thread_Manager> when the thread exits. + return 0; +} + +int +Thread_Pool::open (void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) producer start, dumping the Thread_Pool\n"))); + this->dump (); + + // Create a pool of worker threads. + if (this->activate (THR_NEW_LWP, + this->n_threads_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("activate failed")), + -1); + return 0; +} + +// Activate the task's thread pool, produce the messages that are +// consumed by the threads in the thread pool, and demonstate how to +// shutdown using the <ACE_Message_Queue::deactivate> method. + +int +Thread_Pool::test_queue_deactivation_shutdown (void) +{ + if (this->open () == -1) + return -1; + + ACE_Message_Block *mb = 0; + + // Run the main loop that generates messages and enqueues them into + // the pool of threads managed by <ACE_Task>. + + for (size_t count = 0; + ; + count++) + { + ssize_t n = 0; + + // Allocate a new message. + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ, + ACE_Message_Block::MB_DATA, + 0, + 0, + 0, + &this->lock_adapter_), + -1); + + if (manual) + { +#if !defined (ACE_HAS_WINCE) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) enter a new message for ") + ACE_TEXT ("the task pool..."))); + n = ACE_OS::read (ACE_STDIN, + mb->wr_ptr (), + mb->size ()); +#endif // ACE_HAS_WINCE + } + else + { + static size_t count = 0; + + ACE_OS::sprintf (reinterpret_cast<ACE_TCHAR *> (mb->wr_ptr ()), + ACE_SIZE_T_FORMAT_SPECIFIER, + count); + n = ACE_OS::strlen (mb->rd_ptr ()); + + if (count == n_iterations) + n = 1; // Indicate that we need to shut down. + else + count++; + + if (count == 0 || (count % 20 == 0)) + ACE_OS::sleep (1); + } + + if (n > 1) + { + // Send a normal message to the waiting threads and continue + // producing. + mb->wr_ptr (n * sizeof (ACE_TCHAR)); + + // Pass the message to the Thread_Pool. + if (this->put (mb) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("put"))); + } + else + { + // Release the <Message_Block> since we're shutting down and + // don't need it anymore. + + mb->release (); + // Deactivate the message queue and return. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n(%t) deactivating queue for %d threads, ") + ACE_TEXT ("dump of task:\n"), + this->thr_count ())); + this->dump (); + + // Deactivate the queue. + return this->msg_queue ()->deactivate (); + } + } +} + +// Activate the task's thread pool, produce the messages that are, +// produce the messages that are consumed by the threads in the thread +// pool, and demonstrate how to shutdown by enqueueing "empty" +// messages into the queue. + +int +Thread_Pool::test_empty_message_shutdown (void) +{ + if (this->open () == -1) + return -1; + + ACE_Message_Block *mb = 0; + + // Run the main loop that generates messages and enqueues them into + // the pool of threads managed by <ACE_Task>. + + for (size_t count = 0; + ; + count++) + { + ssize_t n = 0; + + // Allocate a new message. + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ, + ACE_Message_Block::MB_DATA, + 0, + 0, + 0, + &this->lock_adapter_), + -1); + + if (manual) + { +#if !defined (ACE_HAS_WINCE) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) enter a new message for ") + ACE_TEXT ("the task pool..."))); + n = ACE_OS::read (ACE_STDIN, + mb->wr_ptr (), + mb->size ()); +#endif // ACE_HAS_WINCE + } + else + { + static size_t count = 0; + + ACE_OS::sprintf (reinterpret_cast<ACE_TCHAR *> (mb->wr_ptr ()), + ACE_SIZE_T_FORMAT_SPECIFIER, + count); + n = ACE_OS::strlen (mb->rd_ptr ()); + + if (count == n_iterations) + n = 1; // Indicate that we need to shut down. + else + count++; + + if (count == 0 || (count % 20 == 0)) + ACE_OS::sleep (1); + } + + if (n > 1) + { + // Send a normal message to the waiting threads and continue + // producing. + mb->wr_ptr (n * sizeof (ACE_TCHAR)); + + // Pass the message to the Thread_Pool. + if (this->put (mb) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("put"))); + } + else + { + // Send a shutdown message to the waiting threads and return. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n(%t) sending shutdown message to %d threads, ") + ACE_TEXT ("dump of task:\n"), + this->thr_count ())); + this->dump (); + + size_t i = 0; + + // Enqueue an empty message to flag each consumer thread to + // inform it to shutdown. + for (i = this->thr_count (); + i > 0; + i--) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) end of input, ") + ACE_TEXT ("enqueueing \"empty\" message %d\n"), + i)); + + // Note the use of reference counting to avoid copying + // the message contents. + ACE_Message_Block *dup = mb->duplicate (); + + if (this->put (dup) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("put"))); + } + + mb->release (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n(%t) end loop, dump of task:\n"))); + this->dump (); + + return 0; + } + } +} + +#endif /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Thread_Pool_Test")); + +#if defined (ACE_HAS_THREADS) + int n_threads = ACE_MAX_THREADS; + + // Create the worker tasks. + Thread_Pool thread_pool (n_threads); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) running test with %d threads\n"), + n_threads)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) starting empty message shutdown test\n"))); + + // Activate the task's thread pool, produce the messages that are, + // produce the messages that are consumed by the threads in the + // thread pool, and demonstrate how to shutdown by enqueueing + // "empty" messages into the queue. + if (thread_pool.test_empty_message_shutdown () == -1) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) waiting for worker tasks to finish...\n"))); + // Wait for all the threads to reach their exit point, at which + // point the barrier in the destructor of the <ACE_Task> portion of + // <Thread_Pool> will return. + if (thread_pool.wait () == -1) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) starting queue deactivation shutdown test\n"))); + + // Activate the task's thread pool, produce the messages that are + // consumed by the threads in the thread pool, and demonstate how to + // shutdown using the <ACE_Message_Queue::deactivate> method. + if (thread_pool.test_queue_deactivation_shutdown () == -1) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) waiting for worker tasks to finish...\n"))); + // Wait for all the threads to reach their exit point, at which + // point the barrier in the destructor of the <ACE_Task> portion of + // <Thread_Pool> will return. + if (thread_pool.wait () == -1) + return 1; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) all worker tasks destroyed, exiting test...\n"))); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Time_Service_Test.cpp b/ACE/tests/Time_Service_Test.cpp new file mode 100644 index 00000000000..faaaf4a5501 --- /dev/null +++ b/ACE/tests/Time_Service_Test.cpp @@ -0,0 +1,121 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Time_Service_Test +// +// = DESCRIPTION +// This example tests the Time Service server and clerk +// components. The test forks and execs two processes to run both +// the clerk and the time server. The clerk and the server +// communicate for a short duration after which the main process +// kills both the processes. No command line arguments are needed +// to run the test. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Process.h" +#include "ace/ACE.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Time_Service_Test, "$Id$") + +#define APPLICATION \ +ACE_TEXT ("..") ACE_DIRECTORY_SEPARATOR_STR \ +ACE_TEXT ("netsvcs") ACE_DIRECTORY_SEPARATOR_STR \ +ACE_TEXT ("servers") ACE_DIRECTORY_SEPARATOR_STR \ +ACE_TEXT ("main") ACE_PLATFORM_EXE_SUFFIX \ +ACE_TEXT (" -f ") ACE_PLATFORM + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Time_Service_Test")); + + // Make sure that the backing store is not there. We need to make + // sure because this test kills the Time Clerk and on some platforms + // the Clerk is not allowed to do a graceful shutdown. By cleaning + // the backing store here, we are sure that we get a fresh start and + // no garbage data from a possible aborted run + ACE_TCHAR backing_store[MAXPATHLEN + 1]; + +#if defined (ACE_DEFAULT_BACKING_STORE) + // Create a temporary file. + ACE_OS::strcpy (backing_store, + ACE_DEFAULT_BACKING_STORE); +#else /* ACE_DEFAULT_BACKING_STORE */ + if (ACE::get_temp_dir (backing_store, + MAXPATHLEN - 17) == -1) // -17 for ace-malloc-XXXXXX + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Temporary path too long, ") + ACE_TEXT ("defaulting to current directory\n"))); + backing_store[0] = 0; + } + + // Add the filename to the end + ACE_OS::strcat (backing_store, + ACE_TEXT ("ace-malloc-XXXXXX")); + +#endif /* ACE_DEFAULT_BACKING_STORE */ + + ACE_OS::unlink (backing_store); + + const ACE_TCHAR *server_cl = APPLICATION ACE_TEXT ("server.conf"); + ACE_Process_Options server_options; + server_options.command_line (server_cl); + ACE_Process server; + + if (server.spawn (server_options) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("%n; %p (%s).\n"), + ACE_TEXT ("Server fork failed"), + server_cl), + -1); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Server forked with pid = %d.\n"), + server.getpid ())); + + ACE_OS::sleep (3); + + const ACE_TCHAR *clerk_cl = APPLICATION ACE_TEXT ("clerk.conf"); + ACE_Process_Options clerk_options; + clerk_options.command_line (clerk_cl); + ACE_Process clerk; + + if (clerk.spawn (clerk_options) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, ACE_TEXT ("%n; %p: (%s).\n"), + ACE_TEXT ("Clerk fork failed"), clerk_cl), -1); + else + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Clerk forked with pid = %d.\n"), + clerk.getpid ())); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Sleeping...\n"))); + ACE_OS::sleep (10); + + if (clerk.terminate () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Terminate failed for clerk.\n")), + -1); + + if (server.terminate () == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Terminate failed for server.\n")), + -1); + + // Because we kill the clerk process, on Win32 it may not do a + // graceful shutdown and the backing store file is left behind. + if (clerk.wait () != 0) + ACE_OS::unlink (backing_store); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/Time_Value_Test.cpp b/ACE/tests/Time_Value_Test.cpp new file mode 100644 index 00000000000..b40cb7c4295 --- /dev/null +++ b/ACE/tests/Time_Value_Test.cpp @@ -0,0 +1,124 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Time_Value_Test.cpp +// +// = DESCRIPTION +// This is a simple test of ACE_Time_Value. No command line arguments +// are needed to run the test. It also tests the ACE_U_LongLong class. +// Because ACE_U_LongLong is used for ACE_hrtime_t on some platforms, +// this seems like a reasonable place to test it. +// +// = AUTHOR +// Prashant Jain <pjain@cs.wustl.edu> and David Levine <levine@cs.wustl.edu> +// +// ============================================================================ + +// Note, for this test the config.h file *must* come first! +#include "ace/config-all.h" + +ACE_RCSID(tests, Time_Value_Test, "$Id$") + +#include "test_config.h" +#include "ace/ACE.h" +#include "ace/Time_Value.h" + +#include "ace/Numeric_Limits.h" + + +int +run_main (int, ACE_TCHAR *[]) +{ + int ret = 0; + + ACE_START_TEST (ACE_TEXT ("Time_Value_Test")); + + ACE_Time_Value tv1; + ACE_Time_Value tv2 (2); + ACE_Time_Value tv3 (100U); + ACE_Time_Value tv4 (1, 1000000); + ACE_Time_Value tv5 (2UL); + ACE_Time_Value tv6 (1, -1000000); + ACE_Time_Value tv7 (static_cast<long> (2.0)); + + // Beware! 2.5 gets truncated to 2! + // NOTE: this is intended to show what happens with + // ACE_Time_Value (2.5). Some compilers, such as g++ 2.7.2.3, + // actually warn about it without the case. + ACE_Time_Value tv8 (static_cast <long> (2.5)); + + // Test assignment operator, tv9 and tv6 must be the same after this + ACE_Time_Value tv9; + tv9 = tv6; + + ACE_ASSERT (tv1 == ACE_Time_Value (0)); + ACE_ASSERT (tv2 < tv3); + ACE_ASSERT (tv2 <= tv2); + ACE_ASSERT (tv2 >= tv4); + ACE_ASSERT (tv5 >= tv6); + ACE_ASSERT (tv2 == ACE_Time_Value (1, 1000000)); + ACE_ASSERT (tv5 == tv4); + ACE_ASSERT (tv2 == tv4); + ACE_ASSERT (tv1 != tv2); + ACE_ASSERT (tv6 == tv1); + ACE_ASSERT (tv5 == tv7); + ACE_ASSERT (tv7 == tv8); // That's right! See above . . . + ACE_ASSERT (tv9 == tv6); + + ACE_Time_Value tv10 (1); + + ACE_ASSERT (tv10.sec() == 1); + + // test multiplication by double + // test simple multiplication + tv1.set (1, 1); + tv2.set (2, 2); + tv1 *= 2.0; + ACE_ASSERT (tv1 == tv2); + tv1.set (1, 1); + tv2.set (static_cast<time_t> (-2), static_cast<suseconds_t> (-2)); + tv1 *= -2.0; + ACE_ASSERT (tv1 == tv2); + + // test usec shift + tv1.set (1, 999999); + tv2.set (19, 999990); + tv1 *= 10.0; + ACE_ASSERT ( tv1 == tv2); + tv1.set (1, 999999); + tv2.set (-19, -999990); + tv1 *= -10.0; + ACE_ASSERT (tv1 == tv2); + + const time_t max_time_t = ACE_Numeric_Limits<time_t>::max (); + const time_t min_time_t = ACE_Numeric_Limits<time_t>::min (); + + // test results near limits + tv1.set ((max_time_t >> 1), 499999); + tv2.set ((-(max_time_t >> 1) << 1), -999998); + tv1 *= -2.0; + ACE_ASSERT (tv1 == tv2); + tv1.set (max_time_t >> 1, 499999); + tv2.set (((max_time_t >> 1) << 1), 999998); + tv1 *= 2.0; + ACE_ASSERT (tv1 == tv2); + + // test saturated result + tv1.set (max_time_t - 1, 499999); + tv2.set (max_time_t, 999999); // ACE_Time_Value::max_time + tv1 *= 10.0; + ACE_ASSERT (tv1 == tv2); + tv1.set (max_time_t - 1, 499999); + tv2.set (min_time_t, -999999); + tv1 *= -10.0; + ACE_ASSERT (tv1 == tv2); + + ACE_END_TEST; + + return ret; +} diff --git a/ACE/tests/Timeprobe_Test.cpp b/ACE/tests/Timeprobe_Test.cpp new file mode 100644 index 00000000000..2935aca22bd --- /dev/null +++ b/ACE/tests/Timeprobe_Test.cpp @@ -0,0 +1,124 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Timeprobe_Test.cpp +// +// = DESCRIPTION +// This is a simple test of ACE Timeprobes. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +//#define ACE_ENABLE_TIMEPROBES +//#define ACE_MT_TIMEPROBES +//#define ACE_TSS_TIMEPROBES + +#include "tests/test_config.h" +#include "ace/Singleton.h" +#include "ace/Synch_Traits.h" +#include "ace/Recursive_Thread_Mutex.h" +#include "ace/Null_Mutex.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Timeprobe.h" + +ACE_RCSID(tests, Timeprobe_Test, "$Id$") + +#if defined (ACE_ENABLE_TIMEPROBES) + +static const char *events_descriptions_0[] = +{ + "Event Zero", + "Event One", + "Event Two", + "Event Three", + "Event Four", + "Event Five", + "Event Six", + "Event Seven", + "Event Eight", + "Event Nine" +}; + +enum +{ + EVENT_ZERO = 0, + EVENT_ONE, + EVENT_TWO, + EVENT_THREE, + EVENT_FOUR, + EVENT_FIVE, + EVENT_SIX, + EVENT_SEVEN, + EVENT_EIGHT, + EVENT_NINE +}; + +static const char *events_descriptions_1[] = +{ + "Work start", + "Work end" +}; + +enum +{ + WORK_START = 100, + WORK_END +}; + +ACE_TIMEPROBE_EVENT_DESCRIPTIONS (events_descriptions_1, WORK_START); +ACE_TIMEPROBE_EVENT_DESCRIPTIONS (events_descriptions_0, EVENT_ZERO); + +#endif /* ACE_ENABLE_TIMEPROBES */ + +static void +work (int time) +{ + ACE_FUNCTION_TIMEPROBE (WORK_START); + ACE_OS::sleep (time); +} + +#if !defined (ACE_HAS_PURIFY) +// Test creation of ACE_Singletons during static object construction. +// Timeprobes can do that, when they're enabled. Purify would notice +// the memory in use at program termination. +static int +create_singleton () +{ + int *i = ACE_Singleton <int, ACE_SYNCH_RECURSIVE_MUTEX>::instance (); + *i = 3; + + return *i; +} + +int static_singleton_creator = create_singleton (); +#endif /* ! ACE_HAS_PURIFY */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Timeprobe_Test")); + + ACE_TIMEPROBE ("Starting Test"); + + for (int i = 0; i < 3; i++) + { + work (i); + ACE_TIMEPROBE (EVENT_ZERO + i); + } + + ACE_TIMEPROBE ("Ending Test"); + + ACE_TIMEPROBE_PRINT; + + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/Timer_Cancellation_Test.cpp b/ACE/tests/Timer_Cancellation_Test.cpp new file mode 100644 index 00000000000..0db3da521ae --- /dev/null +++ b/ACE/tests/Timer_Cancellation_Test.cpp @@ -0,0 +1,162 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Timer_Cancellation_Test.cpp +// +// = DESCRIPTION +// A test to ensure the timer cancellation works correctly. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/Task.h" + +ACE_RCSID(tests, Timer_Cancellation_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class Deadlock : public ACE_Task_Base +{ +public: + + int svc (void); + + int handle_timeout (const ACE_Time_Value ¤t_time, + const void *act); +}; + +int +Deadlock::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Deadlock starts accessing Reactor and Timer Queue....\n"))); + + this->reactor ()->schedule_timer (this, + 0, + ACE_Time_Value (1)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Deadlock completes accessing Reactor and Timer Queue....\n"))); + + return 0; +} + +int +Deadlock::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Deadlock timeout\n"))); + + return 0; +} + +class Event_Handler : public ACE_Event_Handler +{ +public: + + Event_Handler (Deadlock &deadlock); + + int handle_timeout (const ACE_Time_Value ¤t_time, + const void *act); + + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + Deadlock &deadlock_; +}; + +Event_Handler::Event_Handler (Deadlock &deadlock) + : deadlock_ (deadlock) +{ +} + +int +Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event_Handler timeout\n"))); + + return -1; +} + +int +Event_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event_Handler closed\n"))); + + // Activate Deadlock. + this->deadlock_.activate (); + + // Give Deadlock a chance to deadlock... ;-) + ACE_OS::sleep (1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event Handler starts accessing Reactor....\n"))); + + // This is a superfluous call to the Reactor to acquire its lock. + this->reactor ()->max_notify_iterations (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Event Handler completes accessing Reactor....\n"))); + + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Timer_Cancellation_Test")); + + ACE_Reactor reactor (new ACE_TP_Reactor, + 1); + + Deadlock deadlock; + deadlock.reactor (&reactor); + + Event_Handler handler (deadlock); + + // Scheduler a timer to kick things off. + reactor.schedule_timer (&handler, + 0, + ACE_Time_Value (1)); + + // Run the event loop for a while. + ACE_Time_Value timeout (4); + reactor.run_reactor_event_loop (timeout); + + ACE_END_TEST; + + return 0; +} + +#else /* ACE_HAS_THREADS */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Timer_Cancellation_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/Timer_Queue_Reference_Counting_Test.cpp b/ACE/tests/Timer_Queue_Reference_Counting_Test.cpp new file mode 100644 index 00000000000..d0dcafff858 --- /dev/null +++ b/ACE/tests/Timer_Queue_Reference_Counting_Test.cpp @@ -0,0 +1,658 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Timer_Queue_Reference_Counting_Test.cpp +// +// = DESCRIPTION +// This test is used to check reference counting of the Event +// Handler when it interacts with Timer Queues. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Get_Opt.h" +#include "ace/Timer_Queue.h" +#include "ace/Timer_Heap.h" +#include "ace/Timer_List.h" +#include "ace/Timer_Hash.h" +#include "ace/Timer_Wheel.h" +#include "ace/Reactor.h" +#include "ace/Recursive_Thread_Mutex.h" +#include "ace/Null_Mutex.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Timer_Queue_Reference_Counting_Test, "$Id$") + +static int debug = 0; +static const char *one_second_timeout = "one second timeout"; +static const char *two_second_timeout = "two second timeout"; + +namespace +{ + inline void WAIT_FOR_NEXT_EVENT (ACE_Timer_Queue &timer_queue) + { + ACE_Time_Value const earliest_time = timer_queue.earliest_time (); + ACE_Time_Value const time_of_day = timer_queue.gettimeofday (); + if (earliest_time > time_of_day) + { + ACE_OS::sleep (earliest_time - time_of_day); + } + } +} + +class Reference_Counted_Event_Handler : public ACE_Event_Handler +{ +public: + + Reference_Counted_Event_Handler (int expected_number_of_handle_close_calls); + + ~Reference_Counted_Event_Handler (void); + + int handle_timeout (const ACE_Time_Value &, + const void *); + + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask masks); + + int expected_number_of_handle_close_calls_; + int number_of_handle_close_calls_; +}; + +Reference_Counted_Event_Handler::Reference_Counted_Event_Handler (int expected_number_of_handle_close_calls) + : expected_number_of_handle_close_calls_ (expected_number_of_handle_close_calls), + number_of_handle_close_calls_ (0) +{ + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Reference count in Reference_Counted_Event_Handler() is %d\n", + this->reference_count_.value ())); +} + +Reference_Counted_Event_Handler::~Reference_Counted_Event_Handler (void) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Reference count in ~Reference_Counted_Event_Handler() is %d\n", + this->reference_count_.value ())); + + if (this->expected_number_of_handle_close_calls_ != -1) + ACE_ASSERT (this->number_of_handle_close_calls_ == + this->expected_number_of_handle_close_calls_); +} + +int +Reference_Counted_Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *arg) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Reference count in Reference_Counted_Event_Handler::handle_timeout() for arg = %C is %d\n", + (const char *) arg, + this->reference_count_.value ())); + + return 0; +} + +int +Reference_Counted_Event_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask masks) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Reference_Counted_Event_Handler::handle_close() called with handle = %d and masks = %d. " + "Reference count is %d\n", + handle, + masks, + this->reference_count_.value ())); + + ++this->number_of_handle_close_calls_; + + return 0; +} + +void +cancellation (ACE_Timer_Queue &timer_queue, + int repeat_timer, + int cancel_handler, + int second_timer, + int dont_call_handle_close) +{ + int result = 0; + + int expected_number_of_handle_close_calls = -1; + + if (!dont_call_handle_close) + { + if (cancel_handler) + expected_number_of_handle_close_calls = 1; + else if (second_timer) + expected_number_of_handle_close_calls = 2; + else + expected_number_of_handle_close_calls = 1; + } + + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (expected_number_of_handle_close_calls); + + ACE_Event_Handler_var safe_handler (handler); + + long first_timer_id = -1; + long second_timer_id = -1; + + if (repeat_timer) + { + first_timer_id = + timer_queue.schedule (handler, + one_second_timeout, + ACE_Time_Value (1) + timer_queue.gettimeofday (), + ACE_Time_Value (1)); + ACE_ASSERT (first_timer_id != -1); + } + else + { + first_timer_id = + timer_queue.schedule (handler, + one_second_timeout, + ACE_Time_Value (1) + timer_queue.gettimeofday ()); + ACE_ASSERT (first_timer_id != -1); + } + + if (second_timer) + { + second_timer_id = + timer_queue.schedule (handler, + two_second_timeout, + ACE_Time_Value (2) + timer_queue.gettimeofday (), + ACE_Time_Value (2)); + ACE_ASSERT (second_timer_id != -1); + } + + if (cancel_handler) + { + result = + timer_queue.cancel (handler, + dont_call_handle_close); + + if (second_timer) + ACE_ASSERT (result == 2); + else + ACE_ASSERT (result == 1); + } + else + { + result = + timer_queue.cancel (first_timer_id, + 0, + dont_call_handle_close); + ACE_ASSERT (result == 1); + + if (second_timer) + { + result = + timer_queue.cancel (second_timer_id, + 0, + dont_call_handle_close); + ACE_ASSERT (result == 1); + } + } +} + +template <class TIMER_QUEUE> +class cancellation_test +{ +public: + cancellation_test (const char *); +}; + +template <class TIMER_QUEUE> +cancellation_test<TIMER_QUEUE>::cancellation_test (const char *timer_queue_type) +{ + ACE_DEBUG ((LM_DEBUG, + "\nCancellation test for %C\n\n", + timer_queue_type)); + + int configs[][4] = { + { 0, 0, 0, 0, }, + { 0, 0, 0, 1, }, + { 0, 0, 1, 0, }, + { 0, 0, 1, 1, }, + { 0, 1, 0, 0, }, + { 0, 1, 0, 1, }, + { 0, 1, 1, 0, }, + { 0, 1, 1, 1, }, + { 1, 0, 0, 0, }, + { 1, 0, 0, 1, }, + { 1, 0, 1, 0, }, + { 1, 0, 1, 1, }, + { 1, 1, 0, 0, }, + { 1, 1, 0, 1, }, + { 1, 1, 1, 0, }, + { 1, 1, 1, 1, }, + }; + + for (int i = 0; + i < (int) (sizeof configs / (sizeof (int) * 5)); + i++) + { + TIMER_QUEUE timer_queue; + + cancellation (timer_queue, + configs[i][0], + configs[i][1], + configs[i][2], + configs[i][3]); + } +} + +typedef int (*Expire_Function) (ACE_Timer_Queue &timer_queue); + +int +invoke_expire (ACE_Timer_Queue &timer_queue) +{ + return timer_queue.expire (); +} + +int +invoke_one_upcall (ACE_Timer_Queue &timer_queue) +{ + // Get the current time + ACE_Time_Value current_time (timer_queue.gettimeofday () + + timer_queue.timer_skew ()); + + // Look for a node in the timer queue whose timer <= the present + // time. + ACE_Timer_Node_Dispatch_Info dispatch_info; + + if (timer_queue.dispatch_info (current_time, + dispatch_info)) + { + const void *upcall_act = 0; + + // Preinvoke. + timer_queue.preinvoke (dispatch_info, + current_time, + upcall_act); + + // Call the functor + timer_queue.upcall (dispatch_info, + current_time); + + // Postinvoke + timer_queue.postinvoke (dispatch_info, + current_time, + upcall_act); + + // We have dispatched a timer + return 1; + } + + return 0; +} + +void +expire (ACE_Timer_Queue &timer_queue, + Expire_Function expire_function) +{ + int events = 0; + int result = 0; + + Reference_Counted_Event_Handler *handler = + new Reference_Counted_Event_Handler (1); + + ACE_Event_Handler_var safe_handler (handler); + + long timer_id = + timer_queue.schedule (handler, + one_second_timeout, + ACE_Time_Value (1) + timer_queue.gettimeofday (), + ACE_Time_Value (1)); + ACE_ASSERT (timer_id != -1); + + result = + timer_queue.schedule (handler, + two_second_timeout, + ACE_Time_Value (2) + timer_queue.gettimeofday ()); + ACE_ASSERT (result != -1); + + events += 4; + + for (int i = 0; i < events;) + { + WAIT_FOR_NEXT_EVENT (timer_queue); + + result = + expire_function (timer_queue); + + ACE_ASSERT (result >= 0); + + i += result; + } + + timer_queue.cancel (timer_id, 0, 0); +} + +template<class TIMER_QUEUE> +class expire_test +{ +public: + expire_test (const char *); +}; + +template <class TIMER_QUEUE> +expire_test<TIMER_QUEUE>::expire_test (const char *timer_queue_type) +{ + ACE_DEBUG ((LM_DEBUG, + "\nExpire test for %C\n\n", + timer_queue_type)); + + TIMER_QUEUE timer_queue; + + expire (timer_queue, + invoke_expire); +} + +template<class TIMER_QUEUE> +class upcall_test +{ +public: + upcall_test (const char *); +}; + +template <class TIMER_QUEUE> +upcall_test<TIMER_QUEUE>::upcall_test (const char *timer_queue_type) +{ + ACE_DEBUG ((LM_DEBUG, + "\nOne upcall test for %C\n\n", + timer_queue_type)); + + TIMER_QUEUE timer_queue; + + expire (timer_queue, + invoke_one_upcall); +} + +class Simple_Event_Handler : public ACE_Event_Handler +{ +public: + + Simple_Event_Handler (void); + + ~Simple_Event_Handler (void); + + int handle_timeout (const ACE_Time_Value &, + const void *); + + int handle_close (ACE_HANDLE, + ACE_Reactor_Mask); +}; + +Simple_Event_Handler::Simple_Event_Handler (void) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler()\n")); +} + +Simple_Event_Handler::~Simple_Event_Handler (void) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "~Simple_Event_Handler()\n")); +} + +int +Simple_Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *arg) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler::handle_timeout() for arg = %C\n", + (const char *) arg)); + return 0; +} + +int +Simple_Event_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask masks) +{ + if (debug) + ACE_DEBUG ((LM_DEBUG, + "Simple_Event_Handler::handle_close() called with handle = %d and masks = %d.\n", + handle, + masks)); + + delete this; + + return 0; +} + +void +simple (ACE_Timer_Queue &timer_queue) +{ + int events = 0; + int result = 0; + long timer_id = -1; + + { + Simple_Event_Handler *handler = + new Simple_Event_Handler; + + timer_id = + timer_queue.schedule (handler, + one_second_timeout, + ACE_Time_Value (1) + timer_queue.gettimeofday (), + ACE_Time_Value (1)); + ACE_ASSERT (timer_id != -1); + + result = + timer_queue.cancel (timer_id, + 0, + 0); + ACE_ASSERT (result == 1); + } + + { + Simple_Event_Handler *handler = + new Simple_Event_Handler; + + timer_id = + timer_queue.schedule (handler, + one_second_timeout, + ACE_Time_Value (1) + timer_queue.gettimeofday (), + ACE_Time_Value (1)); + ACE_ASSERT (timer_id != -1); + + events += 3; + } + + for (int i = 0; i < events;) + { + WAIT_FOR_NEXT_EVENT (timer_queue); + + result = + timer_queue.expire (); + + ACE_ASSERT (result >= 0); + + i += result; + } + + timer_queue.cancel (timer_id, 0, 0); +} + +template <class TIMER_QUEUE> +class simple_test +{ +public: + simple_test (const char *); +}; + +template <class TIMER_QUEUE> +simple_test<TIMER_QUEUE>::simple_test (const char *timer_queue_type) +{ + ACE_DEBUG ((LM_DEBUG, + "\nSimple test for %C\n\n", + timer_queue_type)); + + TIMER_QUEUE timer_queue; + + simple (timer_queue); +} + +static int heap = 1; +static int list = 1; +static int hash = 1; +static int wheel = 1; +static int hashheap = 1; +static int test_cancellation = 1; +static int test_expire = 1; +static int test_one_upcall = 1; +static int test_simple = 1; + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:b:c:d:e:l:m:n:o:z:")); + + int cc; + while ((cc = get_opt ()) != -1) + { + switch (cc) + { + case 'a': + heap = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + list = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'c': + hash = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + wheel = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'e': + hashheap = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'l': + test_cancellation = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'm': + test_expire = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + test_one_upcall = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'o': + test_simple = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'z': + debug = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\nusage: %s \n\n") + ACE_TEXT ("\t[-a heap] (defaults to %d)\n") + ACE_TEXT ("\t[-b list] (defaults to %d)\n") + ACE_TEXT ("\t[-c hash] (defaults to %d)\n") + ACE_TEXT ("\t[-d wheel] (defaults to %d)\n") + ACE_TEXT ("\t[-e hashheap] (defaults to %d)\n") + ACE_TEXT ("\t[-l test_cancellation] (defaults to %d)\n") + ACE_TEXT ("\t[-m test_expire] (defaults to %d)\n") + ACE_TEXT ("\t[-n test_one_upcall] (defaults to %d)\n") + ACE_TEXT ("\t[-o test_simple] (defaults to %d)\n") + ACE_TEXT ("\t[-z debug] (defaults to %d)\n") + ACE_TEXT ("\n"), + argv[0], + heap, + list, + hash, + wheel, + hashheap, + test_cancellation, + test_expire, + test_one_upcall, + test_simple, + debug)); + return -1; + } + } + + return 0; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Timer_Queue_Reference_Counting_Test")); + + // Validate options. + int result = + parse_args (argc, argv); + if (result != 0) + return result; + + if (test_cancellation) + { + ACE_DEBUG ((LM_DEBUG, + "\nCancellation test...\n\n")); + + if (heap) { cancellation_test<ACE_Timer_Heap> test ("ACE_Timer_Heap"); ACE_UNUSED_ARG (test); } + if (list) { cancellation_test<ACE_Timer_List> test ("ACE_Timer_List"); ACE_UNUSED_ARG (test); } + if (hash) { cancellation_test<ACE_Timer_Hash> test ("ACE_Timer_Hash"); ACE_UNUSED_ARG (test); } + if (wheel) { cancellation_test<ACE_Timer_Wheel> test ("ACE_Timer_Wheel"); ACE_UNUSED_ARG (test); } + if (hashheap) { cancellation_test<ACE_Timer_Hash_Heap> test ("ACE_Timer_Hash_Heap"); ACE_UNUSED_ARG (test); } + } + + if (test_expire) + { + ACE_DEBUG ((LM_DEBUG, + "\nExpire test...\n\n")); + + if (heap) { expire_test<ACE_Timer_Heap> test ("ACE_Timer_Heap"); ACE_UNUSED_ARG (test); } + if (list) { expire_test<ACE_Timer_List> test ("ACE_Timer_List"); ACE_UNUSED_ARG (test); } + if (hash) { expire_test<ACE_Timer_Hash> test ("ACE_Timer_Hash"); ACE_UNUSED_ARG (test); } + if (wheel) { expire_test<ACE_Timer_Wheel> test ("ACE_Timer_Wheel"); ACE_UNUSED_ARG (test); } + if (hashheap) { expire_test<ACE_Timer_Hash_Heap> test ("ACE_Timer_Hash_Heap"); ACE_UNUSED_ARG (test); } + } + + if (test_one_upcall) + { + ACE_DEBUG ((LM_DEBUG, + "\nOne upcall at a time test...\n\n")); + + if (heap) { upcall_test<ACE_Timer_Heap> test ("ACE_Timer_Heap"); ACE_UNUSED_ARG (test); } + if (list) { upcall_test<ACE_Timer_List> test ("ACE_Timer_List"); ACE_UNUSED_ARG (test); } + if (hash) { upcall_test<ACE_Timer_Hash> test ("ACE_Timer_Hash"); ACE_UNUSED_ARG (test); } + if (wheel) { upcall_test<ACE_Timer_Wheel> test ("ACE_Timer_Wheel"); ACE_UNUSED_ARG (test); } + if (hashheap) { upcall_test<ACE_Timer_Hash_Heap> test ("ACE_Timer_Hash_Heap"); ACE_UNUSED_ARG (test); } + } + + if (test_simple) + { + ACE_DEBUG ((LM_DEBUG, + "\nSimple test...\n\n")); + + if (heap) { simple_test<ACE_Timer_Heap> test ("ACE_Timer_Heap"); ACE_UNUSED_ARG (test); } + if (list) { simple_test<ACE_Timer_List> test ("ACE_Timer_List"); ACE_UNUSED_ARG (test); } + if (hash) { simple_test<ACE_Timer_Hash> test ("ACE_Timer_Hash"); ACE_UNUSED_ARG (test); } + if (wheel) { simple_test<ACE_Timer_Wheel> test ("ACE_Timer_Wheel"); ACE_UNUSED_ARG (test); } + if (hashheap) { simple_test<ACE_Timer_Hash_Heap> test ("ACE_Timer_Hash_Heap"); ACE_UNUSED_ARG (test); } + } + + ACE_END_TEST; + + return 0; +} + diff --git a/ACE/tests/Timer_Queue_Test.cpp b/ACE/tests/Timer_Queue_Test.cpp new file mode 100644 index 00000000000..f6a76ee3465 --- /dev/null +++ b/ACE/tests/Timer_Queue_Test.cpp @@ -0,0 +1,667 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Timer_Queue_Test.cpp +// +// = DESCRIPTION +// This is a simple test of <ACE_Timer_Queue> and four of its +// subclasses (<ACE_Timer_List>, <ACE_Timer_Heap>, +// <ACE_Timer_Wheel>, and <ACE_Timer_Hash>). The test sets up a +// bunch of timers and then adds them to a timer queue. The +// functionality of the timer queue is then tested. No command +// line arguments are needed to run the test. +// +// = AUTHORS +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, +// Prashant Jain <pjain@cs.wustl.edu>, and +// Darrell Brunsch <brunsch@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "randomize.h" +#include "ace/Profile_Timer.h" +#include "ace/Timer_Queue.h" +#include "ace/Timer_List.h" +#include "ace/Timer_Heap.h" +#include "ace/Timer_Wheel.h" +#include "ace/Timer_Hash.h" +#include "ace/Timer_Queue.h" +#include "ace/Recursive_Thread_Mutex.h" +#include "ace/Null_Mutex.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Containers_T.h" + +ACE_RCSID(tests, Timer_Queue_Test, "$Id$") + + +// Number of iterations for the performance tests. Some platforms +// have a very high ACE_DEFAULT_TIMERS (HP-UX is 400), so limit this +// to a reasonable run time. +#if (ACE_DEFAULT_TIMERS > 20) +static int max_iterations = 2000; +#else +static int max_iterations = ACE_DEFAULT_TIMERS * 100; +#endif + +// Amount of time between each timer. +// (0 schedules all the timers to expire at exactly the same time.) +// in milliseconds +static int TIMER_DISTANCE = 50; + +// Array of timer ids assigned to us that we need to keep track of. +static long *timer_ids = 0; + +class Example_Handler : public ACE_Event_Handler +{ +public: + Example_Handler (void): close_count_ (0) {} + + virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask mask) + { + ACE_ASSERT (mask == ACE_Event_Handler::TIMER_MASK); + this->close_count_++; + return 0; + } + + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + int *act = (int *) arg; + ACE_ASSERT (*act == 42 || *act == 007); + int result = 0; + + if (*act == 007) + result = -1; // This is the special value to trigger a handle_close + + delete act; + return result; + } + + int close_count_; + // Keeps track of the number of times that <handle_close> is called. +}; + +static void +test_functionality (ACE_Timer_Queue *tq) +{ + Example_Handler eh; + + ACE_ASSERT (tq->is_empty () != 0); + ACE_ASSERT (ACE_Time_Value::zero == ACE_Time_Value (0)); + long timer_id; + long timer_id2; + + // Do a test on earliest_time. + ACE_Time_Value earliest_time = tq->gettimeofday (); + + const void *timer_act = 0; + ACE_NEW (timer_act, int (1)); + timer_id = tq->schedule (&eh, timer_act, earliest_time); + + ACE_OS::sleep (ACE_Time_Value (0, 10)); + + ACE_NEW (timer_act, int (1)); + timer_id2 = tq->schedule (&eh, timer_act, tq->gettimeofday ()); + + long result = tq->earliest_time () == earliest_time; + ACE_ASSERT (result != 0); + + tq->cancel (timer_id, &timer_act); + delete (int *) timer_act; + tq->cancel (timer_id2, &timer_act); + delete (int *) timer_act; + + ACE_ASSERT (tq->is_empty () == 1); + ACE_ASSERT (eh.close_count_ == 0); + + ACE_NEW (timer_act, int (1)); + timer_id = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (timer_id != -1); + ACE_ASSERT (tq->is_empty () == 0); //== + + ACE_NEW (timer_act, int (42)); + result = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (result != -1); + ACE_ASSERT (tq->is_empty () == 0); //== + + ACE_NEW (timer_act, int (42)); + result = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (result != -1); + ACE_ASSERT (tq->is_empty () == 0); //== + + // The following method will trigger a call to <handle_close>. + ACE_ASSERT (eh.close_count_ == 0); + result = tq->cancel (timer_id, &timer_act, 0); + ACE_ASSERT (result == 1); + delete (int *) timer_act; + + ACE_ASSERT (tq->is_empty () == 0); + ACE_ASSERT (eh.close_count_ == 1); + + result = tq->expire (); + ACE_ASSERT (result == 2); + + ACE_NEW (timer_act, int (007)); + result = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (result != -1); + + const void *timer_act1 = 0; + ACE_NEW (timer_act1, int (42)); + result = tq->schedule (&eh, + timer_act1, + tq->gettimeofday () + ACE_Time_Value (100)); + ACE_ASSERT (result != -1); + + const void *timer_act2 = 0; + ACE_NEW (timer_act2, int (42)); + result = tq->schedule (&eh, + timer_act2, + tq->gettimeofday () + ACE_Time_Value (100)); + ACE_ASSERT (result != -1); + + // The following will trigger a call to <handle_close> when it + // cancels the second timer. This happens because the first timer + // has an <act> of 007, which causes eh.handle_timeout () to return + // -1. Since -1 is returned, all timers that use <eh> will be + // cancelled (and <handle_close> will only be called on the first + // timer that is cancelled). + ACE_ASSERT (eh.close_count_ == 1); + + result = tq->expire (); + ACE_ASSERT (result == 1); + ACE_ASSERT (eh.close_count_ == 2); + + ACE_ASSERT (tq->is_empty () != 0); + delete (int *) timer_act2; + delete (int *) timer_act1; + + ACE_NEW (timer_act, int (4)); + timer_id = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (timer_id != -1); + + ACE_NEW (timer_act, int (4)); + timer_id2 = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (timer_id2 != -1); + + // The following method will trigger a call to <handle_close>. + ACE_ASSERT (eh.close_count_ == 2); + result = tq->cancel (timer_id, &timer_act); + ACE_ASSERT (result != -1); + delete (int *) timer_act; + + result = tq->cancel (timer_id2, &timer_act); + ACE_ASSERT (result != -1); + delete (int *) timer_act; + + ACE_ASSERT (eh.close_count_ == 2); // Only one call to handle_close() even though two timers + ACE_ASSERT (tq->is_empty () != 0); + + result = tq->expire (); + ACE_ASSERT (result == 0); + + // This tests to make sure that <handle_close> is called when there + // is only one timer of the type in the queue + ACE_ASSERT (eh.close_count_ == 2); + + ACE_NEW (timer_act, int (007)); + result = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (result != -1); + + result = tq->expire (); + ACE_ASSERT (result == 1); + ACE_ASSERT (eh.close_count_ == 3); + + ACE_NEW (timer_act, int (6)); + timer_id = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (timer_id != -1); + + ACE_NEW (timer_act, int (7)); + timer_id2 = tq->schedule (&eh, + timer_act, + tq->gettimeofday ()); + ACE_ASSERT (timer_id2 != -1); + + ACE_ASSERT (eh.close_count_ == 3); + + result = tq->cancel (timer_id, &timer_act); + ACE_ASSERT (result == 1); + ACE_ASSERT (eh.close_count_ == 3); + delete (int *) timer_act; + + result = tq->cancel (timer_id2, &timer_act); + ACE_ASSERT (result == 1); + ACE_ASSERT (eh.close_count_ == 3); + delete (int *) timer_act; + + result = tq->expire (); + ACE_ASSERT (result == 0); + ACE_ASSERT (eh.close_count_ == 3); +} + +static void +test_performance (ACE_Timer_Queue *tq, + const ACE_TCHAR *test_name) +{ + Example_Handler eh; + ACE_Profile_Timer timer; + int i; + const void *timer_act = 0; + + ACE_ASSERT (tq->is_empty () != 0); + ACE_ASSERT (ACE_Time_Value::zero == ACE_Time_Value (0)); + + // Test the amount of time required to schedule all the timers. + + ACE_Time_Value *times = 0; + ACE_NEW (times, ACE_Time_Value[max_iterations]); + + // Set up a bunch of times TIMER_DISTANCE ms apart. + for (i = 0; i < max_iterations; ++i) + times[i] = tq->gettimeofday() + ACE_Time_Value(0, i * TIMER_DISTANCE * 1000); + + ACE_Time_Value last_time = times[max_iterations-1]; + + timer.start (); + + for (i = 0; i < max_iterations; ++i) + { + ACE_NEW (timer_act, int (42)); + timer_ids[i] = tq->schedule (&eh, + timer_act, + times[i]); + ACE_ASSERT (timer_ids[i] != -1); + } + + ACE_ASSERT (tq->is_empty () == 0); + + timer.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to schedule %d timers for %s\n"), + max_iterations, test_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, et.user_time, et.system_time)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.user_time / ACE_timer_t (max_iterations)) * 1000000)); + + // Test the amount of time required to cancel all the timers. + + timer.start (); + + for (i = max_iterations; i-- != 0; ) + { + tq->cancel (timer_ids[i], &timer_act); + delete (int *) timer_act; + } + + timer.stop (); + + ACE_ASSERT (tq->is_empty () != 0); + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to cancel %d timers for %s\n"), + max_iterations, test_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, et.user_time, et.system_time)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.user_time / ACE_timer_t (max_iterations)) * 1000000)); + + // Test the amount of time required to schedule and expire all the + // timers. + + timer.start (); + + for (i = 0; i < max_iterations; ++i) + { + ACE_NEW (timer_act, int (42)); + long result = tq->schedule (&eh, timer_act, times[i]); + ACE_ASSERT (result != -1); + } + + ACE_ASSERT (tq->is_empty () == 0); + + // Expire all the timers. + tq->expire (last_time + ACE_Time_Value(1)); + + timer.stop (); + + ACE_ASSERT (tq->is_empty () != 0); + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to schedule and expire %d timers for %s\n"), + max_iterations, test_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, et.user_time, et.system_time)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.user_time / ACE_timer_t (max_iterations)) * 1000000)); + + randomize (times, + max_iterations, + static_cast<ACE_RANDR_TYPE> (ACE_OS::time (0L))); + + // Test the amount of time required to randomly cancel all the + // timers. + + for (i = 0; i < max_iterations; ++i) + { + ACE_NEW (timer_act, int (42)); + timer_ids[i] = tq->schedule (&eh, + timer_act, + times[i]); + ACE_ASSERT (timer_ids[i] != -1); + } + + ACE_ASSERT (tq->is_empty () == 0); + + timer.start (); + + for (i = max_iterations - 1; i >= 0; i--) + { + tq->cancel (timer_ids[i], &timer_act); + delete (int *) timer_act; + } + + ACE_ASSERT (tq->is_empty () != 0); + + timer.stop (); + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to randomly cancel %d timers for %s\n"), + max_iterations, + test_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, + et.user_time, + et.system_time)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.user_time / ACE_timer_t (max_iterations)) * 1000000)); + + // Test the amount of time required to randomly schedule all the timers. + + timer.start (); + + for (i = 0; i < max_iterations; ++i) + { + ACE_NEW (timer_act, int (42)); + timer_ids[i] = tq->schedule (&eh, + timer_act, + times[i]); + ACE_ASSERT (timer_ids[i] != -1); + } + + timer.stop (); + + ACE_ASSERT (tq->is_empty () == 0); + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to randomly schedule %d timers for %s\n"), + max_iterations, test_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, + et.user_time, + et.system_time)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.user_time / ACE_timer_t (max_iterations)) * 1000000)); + + // Test the amount of time required to expire all the timers. + + timer.start (); + + tq->expire (last_time + ACE_Time_Value(1)); + + ACE_ASSERT (tq->is_empty ()); + + timer.stop (); + + timer.elapsed_time (et); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time to expire %d randomly scheduled timers for %s\n"), + max_iterations, test_name)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("real time = %f secs, user time = %f secs, system time = %f secs\n"), + et.real_time, et.user_time, et.system_time)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("time per call = %f usecs\n"), + (et.user_time / ACE_timer_t (max_iterations)) * 1000000)); + + delete [] times; +} + +// This test function was contributed with Bugzilla #2447 to test validity +// of ACE_Timer_Heap timer IDs around the boundary of having to enlarge +// the heap. +static void +test_unique_timer_heap_ids (void) +{ + Example_Handler eh; + ACE_Timer_Heap timer_heap (44); + ACE_Time_Value anytime(1); + ACE_Bounded_Set<long> timer_ids (max_iterations); + long timer_id = -1; + bool all_unique = true; + + for (int i = 0; i < 100; ++i) + { + timer_id = timer_heap.schedule (&eh, 0, anytime); + if (timer_id == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Schedule timer %d %p\n"), + i, + ACE_TEXT ("test_unique_timer_heap_ids"))); + continue; + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Schedule timer %d. Timer id = %d\n"), + i, + timer_id)); + if (1 == timer_ids.insert (timer_id)) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Pass %d, id %d is not unique\n"), + i, + timer_id)); + all_unique = false; + } + + if (i == 0 || i == 1 || i == 47 || i == 48) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Free Timer %d. Timer Id = %d\n"), + i, + timer_id)); + timer_heap.cancel (timer_id); + if (timer_id == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Failed to cancel timer"))); + + timer_ids.remove (timer_id); + } + } + + if (all_unique) + ACE_DEBUG ((LM_INFO, ACE_TEXT ("All timer ids were unique.\n"))); + + return; +} + +class Timer_Queue_Stack +{ + // = TITLE + // Keeps track of the <Timer_Queue>s that we're going to test. + // + // = DESCRIPTION + // This data structure is organized as a stack to make it easy to implement. +public: + // = Initialization method + Timer_Queue_Stack (ACE_Timer_Queue *queue, + const ACE_TCHAR *name, + Timer_Queue_Stack *next = 0) + : queue_ (queue), + name_ (name), + next_ (next) + {} + // "Push" a new <queue> on the stack of <queue>s. + + ACE_Timer_Queue *queue_; + // Pointer to the subclass of <ACE_Timer_Queue> that we're testing. + + const ACE_TCHAR *name_; + // Name of the Queue that we're testing. + + Timer_Queue_Stack *next_; + // Pointer to the next <Timer_Queue>. +}; + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Timer_Queue_Test")); + + if (argc > 1) + max_iterations = ACE_OS::atoi (argv[1]); + + // = Perform initializations. + + Timer_Queue_Stack *tq_stack = 0; + + // Add new Timer_Queue implementations here. Note that these will + // be executed in "reverse order". + + // Timer_Hash (Heap) + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_Hash_Heap, + ACE_TEXT ("ACE_Timer_Hash (Heap)"), + tq_stack), + -1); + + // Timer_Hash + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_Hash, + ACE_TEXT ("ACE_Timer_Hash"), + tq_stack), + -1); + + // Timer_stack + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_List, + ACE_TEXT ("ACE_Timer_List"), + tq_stack), + -1); + + // Timer_Wheel without preallocated memory + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_Wheel, + ACE_TEXT ("ACE_Timer_Wheel (non-preallocated)"), + tq_stack), + -1); + + // Timer_Wheel with preallocated memory. + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_Wheel (ACE_DEFAULT_TIMER_WHEEL_SIZE, + ACE_DEFAULT_TIMER_WHEEL_RESOLUTION, + max_iterations), + ACE_TEXT ("ACE_Timer_Wheel (preallocated)"), + tq_stack), + -1); + // Timer_Heap without preallocated memory. + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_Heap, + ACE_TEXT ("ACE_Timer_Heap (non-preallocated)"), + tq_stack), + -1); + + // Timer_Heap with preallocate memory. + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (new ACE_Timer_Heap (max_iterations, 1), + ACE_TEXT ("ACE_Timer_Heap (preallocated)"), + tq_stack), + -1); + + // Timer_Heap without preallocated memory, using high-res time. + (void) ACE_High_Res_Timer::global_scale_factor (); + ACE_Timer_Heap *tq_heap = new ACE_Timer_Heap; + tq_heap->gettimeofday (&ACE_High_Res_Timer::gettimeofday_hr); + ACE_NEW_RETURN (tq_stack, + Timer_Queue_Stack (tq_heap, + ACE_TEXT ("ACE_Timer_Heap (high-res timer)"), + tq_stack), + -1); + + // Create the Timer ID array + ACE_NEW_RETURN (timer_ids, + long[max_iterations], + -1); + + Timer_Queue_Stack *tq_ptr = tq_stack; + + while (tq_ptr != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("**** starting test of %s\n"), + tq_ptr->name_)); + test_functionality (tq_ptr->queue_); + test_performance (tq_ptr->queue_, + tq_ptr->name_); + delete tq_ptr->queue_; + Timer_Queue_Stack *temp = tq_ptr; + tq_ptr = tq_ptr->next_; + delete temp; + } + delete [] timer_ids; + + ACE_DEBUG + ((LM_DEBUG, + ACE_TEXT ("**** starting unique IDs test for ACE_Timer_Heap\n"))); + test_unique_timer_heap_ids (); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/TkReactor_Test.cpp b/ACE/tests/TkReactor_Test.cpp new file mode 100644 index 00000000000..2b7a7634ae8 --- /dev/null +++ b/ACE/tests/TkReactor_Test.cpp @@ -0,0 +1,322 @@ +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// TkReactor_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the possibility to integrate +// ACE to the Tk Main Loop. This program uses ACE_TkReactor class to +// schedule three additional event sources: +// 1. Events from button "Stop Test" (registed with Tk_CreateEventHandler) +// 2. Events from button "Press Me" (registed with Tk_CreateEventHandler) +// 3. Events from X timer (registed with Tk_CreateTimerHandler) +// 4. Events from ACE timer (registed with ACE_TkReactor::schedule_timer) +// 5. Events from the TCP/IP channel using ACE_Acceptor +// No command line arguments are needed to run the test. +// +// = AUTHOR +// Nagarajan Surendran <naga@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID (tests, + TkReactor_Test, + "$Id$") + +#include "ace/Event_Handler.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Service_Config.h" +#include "ace/Thread_Manager.h" +#include "ace/TkReactor/TkReactor.h" +#include "ace/OS_NS_unistd.h" + +#include <tcl.h> +#include <tk.h> + +Tcl_Interp* tcl_interp; +void eval (const char *s) +{ + char buf[BUFSIZ]; + ACE_OS::strcpy (buf,s); + int st = Tcl_GlobalEval(tcl_interp,buf); + if (st != TCL_OK) + { + int n = ACE_OS::strlen(s); + char* wrk = new char[n + 80]; + ACE_OS::sprintf(wrk, "tkerror \"%s\"", s); + Tcl_GlobalEval(tcl_interp, wrk); + delete wrk; + //exit(1); + } +} + +// Port we listen on. +static const u_short SERV_TCP_PORT = 6670; + +// counter for events from "Press Me" button. +static int count1 = 0; + +// counter for events from Tk Timer. +static int count2 = 0; + +// counter for events from ACE Timer. +static int count3 = 0; + +static int quit = 0; +// Callback for "Stop Test" buton - quit the program. +void +Quit (ClientData, XEvent *) +{ + ACE_DEBUG ((LM_DEBUG,"Quit called\n")); + quit = 1; +} + +static void * +client (void *) +{ + char buf[100]; + size_t mes_len; + ACE_OS::sleep (1); + + ACE_DEBUG ((LM_DEBUG, + " (%P) Client: Starting...\n")); + + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connector; + ACE_OS::sprintf (buf, "Client: the life was good!"); + + mes_len = (int) htonl (ACE_OS::strlen (buf) + 1); + + if (connector.connect (stream, + ACE_INET_Addr (SERV_TCP_PORT, + ACE_DEFAULT_SERVER_HOST)) == -1) + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket open")); + + if (stream.send (4, + (void *) &mes_len, + sizeof (size_t), + (void *)buf, + ACE_OS::strlen (buf) + 1) == -1) + + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket send")); + + if (stream.close () == -1) + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket close")); + + ACE_DEBUG ((LM_DEBUG, + "(%P) Client: Message has been sent, about to exit...\n")); + return 0; +} + +// Callback for "Press Me" button. + +static int +#if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION > 3 +inc_count (ClientData client_data, Tcl_Interp *interp,int, const char **) +#else +inc_count (ClientData client_data, Tcl_Interp *interp,int, char **) +#endif +{ + ACE_DEBUG ((LM_DEBUG,"inc_count ")); + char new_string[80]; + + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1++, + count2, + count3); + + // sprintf (command,"set %s %s",(char *)client_data,new_string); + // eval (command); + const char *varValue = Tcl_SetVar (interp,(char *)client_data,new_string,TCL_LEAVE_ERR_MSG); + if (varValue == 0) + return TCL_ERROR; + return TCL_OK; +} + +// Callback for X Timer. + +static void +inc_tmo (ClientData client_data) +{ + char new_string[80]; + + if (count2 > 10) + ACE_OS::exit (0); + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1, + count2++, + count3); + + // sprintf (command,"set %s %s",(char *)client_data,new_string); + // eval (command); + const char *varValue = Tcl_SetVar (tcl_interp,(char *)client_data,new_string,TCL_LEAVE_ERR_MSG); + if (varValue == 0) + ACE_ERROR ((LM_ERROR,"Tcl_SetVar failed in inc_tmo\n")); + + (void) Tk_CreateTimerHandler (1000, + inc_tmo, + client_data); +} + +class EV_handler : public ACE_Event_Handler +{ +public: + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + char new_string[80]; + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1, + count2, + count3++); + + // sprintf (command,"set %s %s",(char *)arg,new_string); + // eval (command); + const char *varValue = Tcl_SetVar (tcl_interp,(char *)arg,new_string,TCL_LEAVE_ERR_MSG); + if (varValue == 0) + ACE_ERROR_RETURN ((LM_ERROR,"Tcl_SetVar failed in handle_timeout\n"),-1); + + return 0; + } +}; + +class Connection_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void *) + { + //FUZZ: enable check_for_lack_ACE_OS + char buf[100]; + int head; + ssize_t ret = this->peer ().recv_n ((char *) &head, + sizeof (int)); + if (ret != sizeof (int)) + ACE_ERROR_RETURN ((LM_ERROR, + "(%P) %p\n", + "read header"), + -1); + + ret = this->peer ().recv_n (buf, + (int) ntohl (head)); + + if (ret != (int) ntohl (head)) + ACE_ERROR_RETURN ((LM_ERROR, + "(%P) %p\n", + "read message"), + -1); + ACE_DEBUG ((LM_DEBUG, + " (%P)Server (ACE_SOCKET channel message): [%s]\n", + buf)); + return 0; + } +}; + +int +init (Tcl_Interp *interp) +{ + if (Tcl_Init (interp) == TCL_ERROR) + return TCL_ERROR; + if (Tk_Init (interp) == TCL_ERROR) + return TCL_ERROR; + return TCL_OK; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("TkReactor_Test")); + + tcl_interp = Tcl_CreateInterp (); + + if (init (tcl_interp) != TCL_OK) { + ACE_OS::exit (1); + } + + Tk_Window tk = 0; + tk = Tk_MainWindow(tcl_interp); + if (tk == 0) + { + ACE_ERROR_RETURN ((LM_ERROR, "Tk_Reactor_Test: %s\n", tcl_interp->result),1); + } + + char tcl_cmd[] = "source TkReactor_Test.tcl"; + if (Tcl_Eval (tcl_interp, tcl_cmd) != TCL_OK) { + ACE_OS::exit (1); + } + // set up callback + char label_var_name[] = "label_var"; + char pressme[] = "pressme"; + Tcl_CreateCommand (tcl_interp, + pressme, + inc_count, + label_var_name, + 0); + + // Register callback for X Timer + (void) Tk_CreateTimerHandler (1000, + inc_tmo, + label_var_name); + + // It will perform Tk Main Loop + ACE_TkReactor reactor; + ACE_Reactor r (&reactor); + + //Event Handler for ACE Timer. + EV_handler evh; + + ACE_Acceptor <Connection_Handler, ACE_SOCK_ACCEPTOR> acceptor; + + if (acceptor.open (ACE_INET_Addr ((u_short) SERV_TCP_PORT), + &r) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open"), + -1); + + if (reactor.schedule_timer (&evh, + (const void *) "label_var", + ACE_Time_Value (2), + ACE_Time_Value (2))==-1) + ACE_ERROR_RETURN ((LM_ERROR, + " (%P|%t) can't register with reactor\n"), + -1); + + ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) client, + 0, + THR_NEW_LWP | THR_DETACHED); + + while (!quit) + { + int result = reactor.handle_events (); + switch (result) + { + case 0: + // ACE_DEBUG ((LM_DEBUG,"handle_events timed out\n")); + break; + case -1: + ACE_DEBUG ((LM_DEBUG,"handle_events returned -1\n")); + quit = 1; + break; + } + } + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/TkReactor_Test.tcl b/ACE/tests/TkReactor_Test.tcl new file mode 100644 index 00000000000..2d9207dc126 --- /dev/null +++ b/ACE/tests/TkReactor_Test.tcl @@ -0,0 +1,15 @@ +# This file works in conjunction with the C++ source code in TkReactor_Test.cpp + + +# Create a frame. +set label_var "label for all events" + +# Add the new dude button. +button .pressme -text "Press me" -command pressme + +# Add a quit button. +button .goodbye -text "Quit" -command exit + +label .label_for_event_one -textvariable label_var + +pack .label_for_event_one .goodbye .pressme -side top -anchor w diff --git a/ACE/tests/Token_Strategy_Test.cpp b/ACE/tests/Token_Strategy_Test.cpp new file mode 100644 index 00000000000..5bfb46364cc --- /dev/null +++ b/ACE/tests/Token_Strategy_Test.cpp @@ -0,0 +1,248 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program tests the behavior of ACE_Token under a variety of scenarios +// in order verify whether or not tokens are returned, and threads run, in +// a LIFO or FIFO manner. +// +// = AUTHOR +// Don Hinton <dhinton@ieee.org> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Token.h" +#include "ace/Task.h" +#include "ace/Atomic_Op.h" +#include "ace/Auto_IncDec_T.h" +#include "ace/Vector_T.h" +#include "ace/Stats.h" +#include "ace/ACE.h" +#include "ace/Barrier.h" + +ACE_RCSID(tests, Token_Strategy_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class Token_Strategy_Test : public ACE_Task<ACE_MT_SYNCH> +{ +public: + + Token_Strategy_Test (ACE_Token::QUEUEING_STRATEGY strategy = ACE_Token::FIFO, + int threads = 5, int invocations = 10); + ~Token_Strategy_Test (void); + + //FUZZ: disable check_for_lack_ACE_OS + int open (void *a = 0); + //FUZZ: enable check_for_lack_ACE_OS + + int svc (void); + +private: + // Number of threads for the test, must be 5 or more. + int threads_; + + // Barrier used to try to synchronize the for loop in the svc() method. + ACE_Barrier barrier_; + + // Token used to synchonize for loop. + ACE_Token token_; + + // Token strategy to use, LIFO/FIFO. + ACE_Token::QUEUEING_STRATEGY strategy_; + + // Number of loops. + int invocations_; + + // Vector of token counts, one per thread. + ACE_Vector<ACE_INT32> vec_token_count_; + + // This keeps a count of the number of threads who have the token--should always + // be 0 or 1; + ACE_Atomic_Op<ACE_Thread_Mutex, int> counter_; + + // Number of active threads in svc() method. + ACE_Atomic_Op<ACE_Thread_Mutex, int> active_; + + // Errors count, set in svc() and returned from open(). + ACE_Atomic_Op<ACE_Thread_Mutex, int> errors_; + + ACE_UNIMPLEMENTED_FUNC (Token_Strategy_Test (const Token_Strategy_Test &)) + ACE_UNIMPLEMENTED_FUNC (Token_Strategy_Test &operator= (const Token_Strategy_Test &)) +}; + + +Token_Strategy_Test::Token_Strategy_Test (ACE_Token::QUEUEING_STRATEGY strategy, int threads, int invocations) + : threads_ (threads < 5 ? 5 : threads), // need at least 5 threads to satisfy test conditions. + barrier_ (threads_), + strategy_ (strategy), + invocations_ (invocations < 10 ? 10 : invocations), // insure we loop at least a few times. + vec_token_count_ (threads_) +{ + this->counter_ = 0; + this->active_ = 0; + this->errors_ = 0; + + // Initialize the per thread counters used for generating stats. + for (int i = 0; i < this->threads_; ++i) + { + const ACE_UINT32 sample = 0; + this->vec_token_count_.push_back (sample); + } + + this->token_.queueing_strategy (this->strategy_); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (tid = %t) Token_Test::Token_Test (\n") + ACE_TEXT (" token_type = %s\n") + ACE_TEXT (" thread = %d\n") + ACE_TEXT (" invocations = %d\n"), + this->strategy_ == ACE_Token::FIFO ? ACE_TEXT ("FIFO") : ACE_TEXT ("LIFO"), + this->threads_, + this->invocations_)); +} + +Token_Strategy_Test::~Token_Strategy_Test (void) +{} + +int +Token_Strategy_Test::open (void *) +{ + // spawn threads in ace task... + // Make this Task into an Active Object. + this->activate (THR_BOUND | THR_DETACHED, this->threads_); + + // Wait for all the threads to exit. + this->thr_mgr ()->wait (); + return this->errors_.value (); +} + +int +Token_Strategy_Test::svc (void) +{ + int current = this->active_.value (); + ACE_Auto_IncDec<ACE_Atomic_Op<ACE_Thread_Mutex, int> > active_counter (this->active_); + this->barrier_.wait (); + + + //ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (tid = %t) starting loop\n"))); + for (int i = 0; i < this->invocations_; i++) + { + ACE_GUARD_RETURN (ACE_Token, lock, this->token_, -1); + this->vec_token_count_[current]++; + ACE_Auto_IncDec<ACE_Atomic_Op<ACE_Thread_Mutex, int> > token_count_counter (this->counter_); + + // Turn this on to watch each thread grab the token. LIFO has the interesting + // behavior that two thread seem to take turns while all the other threads wait. + if (0) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (tid = %t) token count = %d, ") + ACE_TEXT ("waiters = %d, loop: %d/%d\n"), + this->counter_.value (), + this->token_.waiters (), i + 1, + this->invocations_)); + + // Yield, then simulate some work in order to give the other threads a chance to queue up. + ACE_Thread::yield (); + for (int k = 0; k != 100; ++k) + { + ACE::is_prime (k, 2, k/2); + } + + // If we are the first thread to finish, compute the stats. + if (i + 1 == this->invocations_) + { + if (this->active_ == this->threads_) + { + ACE_Stats stats; + ACE_Stats_Value std_dev (2); + ACE_Stats_Value mean (2); + for (int i = 0; i < this->threads_; ++i) + { + stats.sample (this->vec_token_count_[i]); + } + + //stats.print_summary (2); + stats.std_dev (std_dev); + stats.mean (mean); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (tid = %t) mean = %d.%d, std_dev = %d.%d, max = %d, min = %d\n"), + mean.whole (), mean.fractional (), std_dev.whole (), std_dev.fractional (), + stats.max_value (), stats.min_value ())); + + // These are pretty simplistic tests, so let me know if you have a better idea. + // The assumption is that the standard deviation will be small when using the + // FIFO strategy since all threads will share the token more or less evenly. + // In contrast, the LIFO strategy will allow the two threads to alternate, thus + // several threads will have a low, or zero, token count and create a low mean and + // high standard deviation. If the the thread count is over say 4 or 5, the + // standard deviation will actually excide the mean, hence the test. + if (this->strategy_ == ACE_Token::LIFO && + (mean.whole () > std_dev.whole () && + mean.fractional () > std_dev.fractional ())) + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT (" (tid = %t) LIFO: mean greater than std_dev.\n"))); + this->errors_++; + } + if (this->strategy_ == ACE_Token::FIFO && + (mean.whole () < std_dev.whole () && + mean.fractional () < std_dev.fractional ())) + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT (" (tid = %t) FIFO: mean less than std_dev.\n"))); + this->errors_++; + } + } + } + } + return 0; +} + + +int run_test (ACE_Token::QUEUEING_STRATEGY strategy, int threads = 5, + int invocations = 10) +{ + Token_Strategy_Test test (strategy, threads, invocations); + return test.open () == 0 ? 0 : 1; +} + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Token_Strategy_Test")); + int retval = 0; + + if (argc > 3) + { + // print usage + retval = 1; + } + else + { + int threads = 5; + int invocations = 100; + + if (argc > 1) threads = ACE_OS::atoi (argv[1]); + if (argc > 2) invocations = ACE_OS::atoi (argv[2]); + + // New test using ACE_Token::queueing_strategy () + retval += run_test (ACE_Token::FIFO, threads, invocations); + retval += run_test (ACE_Token::LIFO, threads, invocations); + } + + ACE_END_TEST; + return retval; +} + +#else /* ACE_HAS_THREADS */ +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Token_Strategy_Test: your platform doesn't support threads\n")), 1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/tests/Tokens_Test.cpp b/ACE/tests/Tokens_Test.cpp new file mode 100644 index 00000000000..434b0b6c477 --- /dev/null +++ b/ACE/tests/Tokens_Test.cpp @@ -0,0 +1,330 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Tokens_Test.cpp +// +// = DESCRIPTION +// This application tests the ACE Token library including local +// and remote readers/writer and mutex locks, and token +// collections. This is accomplished with the ACE Token Invariant +// utilities that allow and application to check that +// readers/writer and mutex lock invariants are always satisfied. +// Throughout this test, ACE_ASSERTs are used in conjunction with +// Token Invariant operations, so that errors are reported using +// the ACE tests convention. This application performs a local +// test and then fork_execs a token server and performs the same +// test remotely. +// +// = AUTHOR +// Tim Harrison <harrison@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Process.h" +#include "ace/Get_Opt.h" +#include "ace/Local_Tokens.h" +#include "ace/Token_Collection.h" +#include "ace/Remote_Tokens.h" +#include "ace/Thread_Manager.h" +#include "ace/Token_Invariants.h" +#include "ace/Barrier.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, Tokens_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_TOKENS_LIBRARY) + +typedef ACE_Token_Invariant_Manager TOKEN_INVARIANTS; + +static const ACE_TCHAR *server_host = ACE_DEFAULT_SERVER_HOST; +static const int server_port = 23456; + +// Synchronize starts of threads, so that they all start before one +// has a chance to finish and clean up the TSS objects. To avoid +// creating a static object, it is dynamically allocated, before +// spawning any threads. +static ACE_Barrier *thread_start; + +struct Test_Params +{ +public: + ACE_Token_Proxy *token1_; + ACE_Token_Proxy *token2_; + const ACE_TCHAR *collection_name_; +}; + +static void * +run_thread (void *vp) +{ + Test_Params *tp = (Test_Params *) vp; + ACE_Token_Collection collection (1, tp->collection_name_); + collection.insert (*(tp->token1_)); + collection.insert (*(tp->token2_)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) new thread.\n"))); + thread_start->wait (); + + int count = 50; + while (count--) + { + if (collection.acquire () == -1) + { + if (ACE_OS::last_error () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("deadlock detected in acquire"))); + continue; + } + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p acquire failed\n"), + ACE_TEXT ("run_thread"))); + return (void *) -1; + } + + ACE_ASSERT ((TOKEN_INVARIANTS::instance ()->acquired (tp->token1_) == 1) || + (TOKEN_INVARIANTS::instance ()->acquired (tp->token2_) == 1)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %s acquired.\n"), + collection.name ())); + + TOKEN_INVARIANTS::instance ()->releasing (tp->token1_); + TOKEN_INVARIANTS::instance ()->releasing (tp->token2_); + + if (collection.renew () == -1) + { + if (ACE_OS::last_error () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("deadlock detected"))); + goto deadlock; + } + ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%t) %p renew failed\n"), + ACE_TEXT ("run_thread"))); + return (void *) -1; + } + + ACE_ASSERT (TOKEN_INVARIANTS::instance ()->acquired (tp->token1_) == 1 || + TOKEN_INVARIANTS::instance ()->acquired (tp->token2_) == 1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %s renewed.\n"), + collection.name ())); + + deadlock: + + TOKEN_INVARIANTS::instance ()->releasing (tp->token1_); + TOKEN_INVARIANTS::instance ()->releasing (tp->token2_); + + if (collection.release () == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p release failed\n"), + ACE_TEXT ("run_thread"))); + return (void *) -1; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) %s released.\n"), + collection.name ())); + } + + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) thread finished.\n"))); + + return 0; +} + +static int +run_test (ACE_Token_Proxy *A, + ACE_Token_Proxy *B, + ACE_Token_Proxy *R, + ACE_Token_Proxy *W) +{ + // Parameters to be passed to the threads. + Test_Params tp1, tp2, tp3; + + // tp1 and tp2 can run concurrently. Neither tp1 or tp3 can run + // when tp2 is running. + tp1.collection_name_ = ACE_TEXT ("A and Reader"); + tp1.token1_ = A; + tp1.token2_ = R; + + tp2.collection_name_ = ACE_TEXT ("A and Writer"); + tp2.token1_ = A; + tp2.token2_ = W; + + tp3.collection_name_ = ACE_TEXT ("B and Reader"); + tp3.token1_ = B; + tp3.token2_ = R; + + // Spawn off three threads. + ACE_Thread_Manager *mgr = ACE_Thread_Manager::instance (); + + if (mgr->spawn (ACE_THR_FUNC (run_thread), + (void *) &tp1, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn 1 failed")), + -1); + + if (mgr->spawn (ACE_THR_FUNC (run_thread), + (void *) &tp2, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn 2 failed")), + -1); + + if (mgr->spawn (ACE_THR_FUNC (run_thread), + (void *) &tp3, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawn 3 failed")), -1); + + // Wait for all threads to exit. + mgr->wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Test finished.\n"))); + return 0; +} +#endif /* ACE_HAS_THREADS && ACE_HAS_TOKENS_LIBRARY */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Tokens_Test")); +#if defined (ACE_HAS_THREADS) +#if defined (ACE_HAS_TOKENS_LIBRARY) + ACE_Token_Proxy *A = 0, *B = 0, *R = 0, *W = 0; + + ACE_NEW_RETURN (A, + ACE_Local_Mutex (ACE_TEXT ("L Mutex A"), 0, 0), + -1); + ACE_NEW_RETURN (B, + ACE_Local_Mutex (ACE_TEXT ("L Mutex B"), 0, 0), + -1); + ACE_NEW_RETURN (R, + ACE_Local_RLock (ACE_TEXT ("L Reader Lock"), 0, 0), + -1); + ACE_NEW_RETURN (W, + ACE_Local_WLock (ACE_TEXT ("L Writer Lock"), 0, 0), + -1); + ACE_NEW_RETURN (thread_start, + ACE_Barrier (3), + -1); + + run_test (A, B, R, W); + + const ACE_TCHAR *token_exe = + ACE_TEXT ("..") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("netsvcs") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("servers") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("main") ACE_PLATFORM_EXE_SUFFIX; + + int status = ACE_OS::access (token_exe, F_OK); + if (status == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + token_exe)); + else + { + const ACE_TCHAR *cl = + ACE_TEXT ("..") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("netsvcs") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("servers") ACE_DIRECTORY_SEPARATOR_STR + ACE_TEXT ("main") ACE_PLATFORM_EXE_SUFFIX + ACE_TEXT (" -f ") ACE_PLATFORM + ACE_TEXT ("tokens.conf"); + + ACE_Process_Options options; + options.command_line (cl); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Forking Token Service.\n"))); + + // Start up the token server for the remote test. + ACE_Process new_process; + if (new_process.spawn (options) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%n; %p (%s), will not run remote test.\n"), + ACE_TEXT ("Server fork failed"), + cl)); + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Server forked with pid = %d.\n"), + new_process.getpid ())); + + // Wait for the server to start. + ACE_OS::sleep (3); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Using Token Server on %s at port %d.\n"), + server_host, + server_port)); + ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, + server_host)); + + delete A; + delete B; + delete R; + delete W; + + ACE_NEW_RETURN (A, + ACE_Remote_Mutex (ACE_TEXT ("R Mutex A"), 0, 1), + -1); + ACE_NEW_RETURN (B, + ACE_Remote_Mutex (ACE_TEXT ("R Mutex B"), 0, 1), + -1); + ACE_NEW_RETURN (R, + ACE_Remote_RLock (ACE_TEXT ("R Reader Lock"), 0, 1), + -1); + ACE_NEW_RETURN (W, + ACE_Remote_WLock (ACE_TEXT ("R Writer Lock"), 0, 1), + -1); + + run_test (A, B, R, W); + + // Wait for the server to finish. + ACE_OS::sleep (3); + + // Kill the token server. + if (new_process.terminate () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Kill failed.\n")), + -1); + } + + delete thread_start; + thread_start = 0; + delete A; + delete B; + delete R; + delete W; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) main thread exiting.\n"))); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("ACE must be compiled with -DACE_HAS_TOKENS_LIBRARY to run this test\n"))); +#endif /* ACE_HAS_TOKENS_LIBRARY */ +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/UNIXclerk.conf b/ACE/tests/UNIXclerk.conf new file mode 100644 index 00000000000..6ea52b0b52b --- /dev/null +++ b/ACE/tests/UNIXclerk.conf @@ -0,0 +1,3 @@ +# Note: hostname and port number need to be concatenated separated by ":" +dynamic Logging_Strategy Service_Object * ACE:_make_ACE_Logging_Strategy() "-s log/Time_Service_Test_Clerk.log -f OSTREAM" +dynamic Time_Server_Test Service_Object * netsvcs:_make_ACE_TS_Clerk_Processor () "-h localhost:10222 -t 4" diff --git a/ACE/tests/UNIXserver.conf b/ACE/tests/UNIXserver.conf new file mode 100644 index 00000000000..5dcfa378156 --- /dev/null +++ b/ACE/tests/UNIXserver.conf @@ -0,0 +1,7 @@ +# These are the services that can be linked into ACE. +# You can replace the hardcoded "-p xxxxx" with "-p +# $PORT" if you set your PORT environment variable. + +dynamic Logging_Strategy Service_Object * ACE:_make_ACE_Logging_Strategy() "-s log/Time_Service_Test_Server.log -f OSTREAM" +dynamic Time_Service Service_Object * netsvcs:_make_ACE_TS_Server_Acceptor() "-p 10222" + diff --git a/ACE/tests/UNIXtokens.conf b/ACE/tests/UNIXtokens.conf new file mode 100644 index 00000000000..d15155dc60d --- /dev/null +++ b/ACE/tests/UNIXtokens.conf @@ -0,0 +1,5 @@ +# Solaris version + +dynamic Logging_Strategy Service_Object * ACE:_make_ACE_Logging_Strategy() "-s log/Tokens_Test_Server.log -f OSTREAM" +dynamic Token_Service Service_Object * netsvcs:_make_ACE_Token_Acceptor() "-p 23456" + diff --git a/ACE/tests/UPIPE_SAP_Test.cpp b/ACE/tests/UPIPE_SAP_Test.cpp new file mode 100644 index 00000000000..296d746fbb6 --- /dev/null +++ b/ACE/tests/UPIPE_SAP_Test.cpp @@ -0,0 +1,184 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// UPIPE_SAP_Test.cpp +// +// = DESCRIPTION +// This is a test that uses <ACE_UPIPE_SAP> and <ACE_Thread> for +// intra-process communication. +// +// = AUTHOR +// Gerhard Lenzer <Gerhard.Lenzer@med.siemens.de>, +// Douglas C. Schmidt <schmidt@cs.wustl.edu>, and +// Prashant Jain <pjain@cs.wustl.edu> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Stream.h" +#include "ace/UPIPE_Acceptor.h" +#include "ace/UPIPE_Connector.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(tests, UPIPE_SAP_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) && \ + (defined (ACE_HAS_STREAM_PIPES) || defined (ACE_HAS_WIN32_NAMED_PIPES)) + +// Global pattern +static ACE_UPIPE_Addr addr (ACE_TEXT ("pattern")); + +// connector thread. + +static void * +connector (void *) +{ + ACE_UPIPE_Stream c_stream; + + ACE_OS::sleep (5); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) connector starting connect\n"))); + ACE_UPIPE_Connector con; + + if (con.connect (c_stream, addr) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) connector ACE_UPIPE_Connector failed\n"))); + + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, ACE_Message_Block (sizeof ("hello thanks") * sizeof (char)), 0); + + mb->copy ("hello"); + + if (c_stream.send (mb) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) error connector send\n"))); + + if (c_stream.recv (mb) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) error connector recv\n"))); + + ACE_ASSERT (ACE_OS::strcmp (mb->rd_ptr (), "thanks") == 0); + + // Free up the memory block. + mb->release (); + + // Now try the send()/recv() interface. + char mytext[] = "This string is sent by connector as a buffer"; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) connector sending text\n"))); + if (c_stream.send (mytext, sizeof (mytext)) == -1) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) buffer send from connector failed\n"))); + + char conbuf[BUFSIZ]; // Buffer to receive response. + + int i = 0; + + for (char c = ' '; c != '!'; i++) + { + if (c_stream.recv (&c, 1) == -1) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) buffer recv from connector failed\n"))); + else + conbuf[i] = c; + } + + conbuf[i] = '\0'; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) conbuf = %s\n"), conbuf)); + ACE_ASSERT (ACE_OS::strcmp (conbuf, "this is the acceptor response!") == 0); + + c_stream.close (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) exiting thread\n"))); + return 0; +} + +static void * +acceptor (void *args) +{ + ACE_UPIPE_Acceptor *acceptor = (ACE_UPIPE_Acceptor *) args; + ACE_UPIPE_Stream s_stream; + + if (acceptor->accept (s_stream) == -1) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) ACE_UPIPE_Acceptor.accept failed\n"))); + + ACE_Message_Block *mb = 0; + + if (s_stream.recv (mb) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) acceptor recv failed\n"))); + + ACE_ASSERT (ACE_OS::strcmp (mb->rd_ptr (), "hello") == 0); + + mb->wr_ptr (mb->rd_ptr ()); + mb->copy ("thanks"); + + if (s_stream.send (mb) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) acceptor send failed\n"))); + + char s_buf[BUFSIZ]; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) acceptor sleeping on recv\n"))); + + if (s_stream.recv (s_buf, sizeof (s_buf)) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) acceptor recv failed\n"))); + else + ACE_ASSERT (ACE_OS::strcmp (s_buf, + "This string is sent by connector as a buffer") == 0); + + const char svr_response[] = "this is the acceptor response!"; + ACE_OS::strcpy (s_buf, svr_response); + + if (s_stream.send (s_buf, sizeof (svr_response)) == -1) + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) acceptor send failed\n"))); + + s_stream.close (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) exiting thread\n"))); + return 0; +} +#endif /* ACE_HAS_THREADS && (ACE_HAS_STREAM_PIPES || ACE_HAS_WIN32_NAMED_PIPES) */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("UPIPE_SAP_Test")); + +#if defined (ACE_HAS_THREADS) && \ + (defined (ACE_HAS_STREAM_PIPES) || defined (ACE_HAS_WIN32_NAMED_PIPES)) + + ACE_UPIPE_Acceptor acc (addr); + + // Spawn a acceptor thread. + if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (acceptor), + (void *) &acc, + THR_NEW_LWP, + 0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn")), 1); + + // Spawn a connector thread. + if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (connector), + (void *) 0, + THR_NEW_LWP, + 0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn")), 1); + + ACE_Thread_Manager::instance ()->wait (); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) joined with acceptor thread\n"))); + + // Close the acceptor + acc.close (); + +#else + +#if !defined (ACE_HAS_THREADS) + ACE_ERROR ((LM_INFO, ACE_TEXT ("threads not supported on this platform\n"))); +#else + ACE_ERROR ((LM_INFO, ACE_TEXT ("UPIPE is not supported on this platform\n"))); +#endif /* !defined (ACE_HAS_THREADS) */ +#endif /* ACE_HAS_THREADS && (ACE_HAS_STREAM_PIPES || ACE_HAS_WIN32_NAMED_PIPES) */ + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/UUID_Test.cpp b/ACE/tests/UUID_Test.cpp new file mode 100644 index 00000000000..6b54ea7d2d0 --- /dev/null +++ b/ACE/tests/UUID_Test.cpp @@ -0,0 +1,129 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// UUID_Test.cpp +// +// = DESCRIPTION +// Test the ACE UUID class which generates unique id's +// +// = AUTHOR +// Andrew T. Finnel <andrew@activesol.net> and +// Yamuna Krishnmaurthy <yamuna@oomworks.com> +// ============================================================================ + +#include "test_config.h" +#include "ace/UUID.h" +#include "ace/Auto_Ptr.h" + +class Tester +{ +public: + int init (void); + int test (void); +}; + + +int +Tester::init (void) +{ + ///Initialise the UUID Generator + ACE_Utils::UUID_GENERATOR::instance ()->init (); + return 0; +} + +int +Tester::test (void) +{ + int retval = 0; + + // Generate UUID + auto_ptr <ACE_Utils::UUID> uuid (ACE_Utils::UUID_GENERATOR::instance ()->generate_UUID ()); + ACE_CString uuid_str (uuid->to_string ()->c_str ()); + ACE_DEBUG ((LM_DEBUG, + "Generated UUID\n %s\n", + uuid_str.c_str ())); + + // Construct UUID from string + ACE_Utils::UUID new_uuid (uuid_str); + ACE_DEBUG ((LM_DEBUG, + "UUID Constructed from above Generated UUID\n %s\n", + new_uuid.to_string ()->c_str ())); + + // Construct UUID from string by assigning it + ACE_Utils::UUID new_uuid_assign; + new_uuid_assign.from_string (new_uuid.to_string ()->c_str ()); + ACE_DEBUG ((LM_DEBUG, + "UUID Constructed from above Generated UUID with assign\n %s\n", + new_uuid_assign.to_string ()->c_str ())); + + if (new_uuid != new_uuid_assign) + { + ACE_ERROR ((LM_ERROR, "Error: UUIDs are not the same\n")); + retval = -1; + } + + // Construct UUID using the copy constructor + ACE_Utils::UUID new_uuid_copy (new_uuid); + ACE_DEBUG ((LM_DEBUG, + "UUID Constructed from above Generated UUID with copy\n %s\n", + new_uuid_copy.to_string ()->c_str ())); + + if (new_uuid != new_uuid_copy) + { + ACE_ERROR ((LM_ERROR, "Error: UUIDs are not the same with copy\n")); + retval = -1; + } + + // Generate UUID with process and thread ids. + auto_ptr <ACE_Utils::UUID> uuid_with_tp_id (ACE_Utils::UUID_GENERATOR::instance ()->generate_UUID (0x0001, + 0xc0)); + ACE_DEBUG ((LM_DEBUG, + "UUID with Thread and Process ID\n %s\n", + uuid_with_tp_id->to_string ()->c_str ())); + + if (new_uuid == *uuid_with_tp_id) + { + ACE_ERROR ((LM_ERROR, "Error: UUIDs are the same\n")); + retval = -1; + } + + // Construct UUID from string + ACE_Utils::UUID new_uuid_with_tp_id (uuid_with_tp_id->to_string ()->c_str ()); + ACE_DEBUG ((LM_DEBUG, + "UUID with Thread and Process ID reconstructed from above UUID \n %s\n", + new_uuid_with_tp_id.to_string ()->c_str ())); + + return retval; +} + +int run_main(int, ACE_TCHAR* []) +{ + ACE_START_TEST (ACE_TEXT ("UUID_Test")); + + Tester tester; + + if (tester.init () == -1) + { + ACE_DEBUG((LM_DEBUG, + "UUIDTest: Tester::init failed\n")); + return -1; + } + + int result = tester.test(); + + if (result == 0) + ACE_DEBUG((LM_DEBUG, + "UUID_Test succeeded\n")); + else + ACE_ERROR((LM_ERROR, + "UUID_Test failed\n")); + + ACE_END_TEST; + + return result; +} diff --git a/ACE/tests/Unbounded_Set_Test.cpp b/ACE/tests/Unbounded_Set_Test.cpp new file mode 100644 index 00000000000..dc4379b7720 --- /dev/null +++ b/ACE/tests/Unbounded_Set_Test.cpp @@ -0,0 +1,135 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Unbounded_Set_Test.cpp +// +// = DESCRIPTION +// This test illustrates the use of ACE_Unbounded_Set. +// No command line arguments are needed to run the test. +// +// = AUTHOR +// Rudolf Weber <rfweber@tesionmail.de>, +// ace/tests integration <Oliver.Kellogg@sysde.eads.net> +// +// ============================================================================ + +#include "test_config.h" +#include <ace/Unbounded_Set.h> +#include <ace/Auto_Ptr.h> +#include <ace/SString.h> + +ACE_RCSID(tests, Unbounded_Set_Test, "$Id$") + +struct MyNode +{ + unsigned k; + MyNode () : k (0) {} + MyNode (int pk) : k (pk) {} + MyNode (const MyNode& o) : k (o.k) {} + bool operator== (const MyNode& o) { return (k == o.k); } +}; + +size_t count_const_set (const ACE_Unbounded_Set<MyNode>& cubs) +{ + size_t number_of_elements = 0; + for (ACE_Unbounded_Set<MyNode>::const_iterator ci (cubs); !ci.done(); ci++) + number_of_elements++; + return number_of_elements; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + int r; + unsigned k; + MyNode node (1); + + ACE_START_TEST (ACE_TEXT ("Unbounded_Set_Test")); + + ACE_Unbounded_Set<MyNode> ubs; + ACE_ASSERT (ubs.size () == 0); + + // Insert a value. Immediately remove it. + r = ubs.insert (node); + ACE_ASSERT (r == 0); + ACE_ASSERT (ubs.size () == 1); + r = ubs.remove (node); + ACE_ASSERT (r == 0); + ACE_ASSERT (ubs.size () == 0); + + // Insert several different values. + for (node.k = 1; node.k <= 5; node.k++) + { + r = ubs.insert (node); + ACE_ASSERT (r == 0); + ACE_ASSERT (ubs.size () == node.k); + } + + // Test assigment of sets. + // To do that, we also test some of the iterator methods. + typedef ACE_Unbounded_Set<MyNode> MySet; + MySet ubs2 = ubs; // Test a typedef of a set. + ACE_ASSERT (ubs2.size() == ubs.size()); + { + MySet::ITERATOR it1 (ubs); + MySet::iterator it2 (ubs2); + for (k = 1; k <= 5; k++) + { + ACE_ASSERT (! it1.done ()); + ACE_ASSERT (! it2.done ()); + MyNode n1 = *it1; + MyNode n2 = *it2; + ACE_ASSERT (n1 == n2); + it1.advance (); + it2.advance (); + } + ACE_ASSERT (it1.done ()); + ACE_ASSERT (it2.done ()); + // Verify that a set may be emptied while an iterator on the set is + // in-scope but inactive: + ubs.reset (); + // Restore original set from ubs2 + ubs = ubs2; + } + + // Selective deletion of elements and element retrieval. + { + MySet::iterator it (ubs2); + int deleted = 0; + while (! it.done ()) + { + MyNode n = *it; + it.advance (); /* Being friendly here: Move the iterator on + so that element removal does not interfere + with the current iterator position. + The less friendly case, removal under the + current iterator position, is below. */ + if (n.k % 2 == 1) + { + r = ubs2.remove (n); + deleted++; + } + } + ACE_ASSERT (ubs2.size () + deleted == ubs.size()); + + MyNode node2 (2); + ACE_ASSERT (ubs2.find (node2) == 0); + + MyNode node3 (3); + ACE_ASSERT (ubs2.find (node3) != 0); + + ubs2.insert (node3); + } + + size_t s = count_const_set (ubs); + ACE_ASSERT (s == ubs.size ()); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/Unload_libACE.cpp b/ACE/tests/Unload_libACE.cpp new file mode 100644 index 00000000000..c13ee8039a3 --- /dev/null +++ b/ACE/tests/Unload_libACE.cpp @@ -0,0 +1,271 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Unload_libACE.cpp +// +// = DESCRIPTION +// This is a simple test of library unloading that uses +// an application which has _not_ been linked with libACE +// but uses dlopen() to dynamically load libACE +// and then uses dlclose() to unload it. +// +// = AUTHORS +// David Smith <dts@prismtech.com> and Don Sharp <Donald.Sharp@prismtech.com> +// +// ============================================================================ + +//FUZZ: disable check_for_lack_ACE_OS +//FUZZ: disable check_for_improper_main_declaration + +#include <stdio.h> + +#undef UNLOAD_LIBACE_TEST + +#if defined (__GNUC__) +#if !defined (ACE_VXWORKS) && !defined (__MINGW32__) && !defined (__CYGWIN32__) +#define UNLOAD_LIBACE_TEST 1 +#endif /* !ACE_VXWORKS && !__MINGW32__ && !CYGWIN32 */ +#endif /* __GNUC__ */ + +#if defined (__hpux) || defined (__SUNPRO_CC) +#define UNLOAD_LIBACE_TEST 1 +#endif /* (__hpux) || (__SUNPRO_CC) */ + +#if defined (ACE_AS_STATIC_LIBS) +#undef UNLOAD_LIBACE_TEST +#endif /* ACE_AS_STATIC_LIBS */ + +#ifdef UNLOAD_LIBACE_TEST + +#include <errno.h> +#include <dlfcn.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#define TIME_STAMP_FIELD_WIDTH 32 + +static char * +time_stamp (char date_and_time[], int date_and_timelen, int format) +{ + static char const *const month_name[] = + { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + + static char const *const day_of_week_name[] = + { + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + }; + + char *ts = 0; + + if (date_and_timelen >= TIME_STAMP_FIELD_WIDTH) + { + time_t timeval; + struct tm *now; + + time (&timeval); + now = localtime (&timeval); /* Get current local time. */ + + if (format == 'Y') + { + sprintf (date_and_time, + "%3s %3s %2d %04d %02d:%02d:%02d.%06d", + day_of_week_name[now->tm_wday], + month_name[now->tm_mon], + (int) now->tm_mday, + (int) now->tm_year + 1900, + (int) now->tm_hour, + (int) now->tm_min, (int) now->tm_sec, (int) 0); + } + else /* 'T' */ + { + sprintf (date_and_time, + "%3s %2d %02d:%02d:%02d.%03d %04d", + month_name[now->tm_mon], + (int) now->tm_mday, + (int) now->tm_hour, + (int) now->tm_min, + (int) now->tm_sec, (int) 0, + (int) now->tm_year + 1900); + } + + ts = date_and_time; + } + return ts; +} + +int +main (int, char **) +{ + char const *const program = "UnloadLibACE"; + + int status = 0; + void *handle = 0; + char *ace_root = 0; + char tbuf[BUFSIZ]; + char ybuf[BUFSIZ]; + FILE *logfp = 0; + + if ((logfp = fopen ("log/UnloadLibACE.log", "w")) != 0) + { + setvbuf (logfp, 0, _IONBF, 0); + // reassign stdout/stderr to log file + int fdno = fileno (logfp); + + dup2 (fdno, fileno (stdout)); + dup2 (fdno, fileno (stderr)); + setvbuf (stdout, 0, _IONBF, 0); + setvbuf (stderr, 0, _IONBF, 0); + fflush (stdout); + fflush (stderr); + + printf ("%s@LM_DEBUG@ Starting %s test at %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), + program, time_stamp (ybuf, BUFSIZ, 'Y')); + + if ((ace_root = getenv ("ACE_ROOT")) != 0) + { + char buf[BUFSIZ]; + + strcpy (buf, ace_root); + strcat (buf, "/lib/lib"); +#if defined (ACE_LIB_NAME) + strcat (buf, ACE_LIB_NAME); +#else + strcat (buf, "ACE"); +#endif /* ACE_LIB_NAME */ +#if defined (__hpux) && !(defined (__ia64) && (__ia64 == 1)) + strcat (buf, ".sl"); +#elif defined (__APPLE__) + strcat (buf, ".dylib"); +#else + strcat (buf, ".so"); +#endif /* (__hpux) */ + + handle = dlopen (buf, RTLD_LAZY); + if (handle == 0) + { + // is it because of "No such file or directory" ? + if (errno != ENOENT) + { + fprintf (stderr, + "%s@LM_ERROR@ dlopen() returned NULL\n", + time_stamp (tbuf, BUFSIZ, 'T')); + fprintf (stderr, + "%s@LM_ERROR@ dlerror() says: %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), dlerror ()); + status = 1; + } + else + { + printf ("%s@LM_DEBUG@ dlopen() did not find %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), buf); + status = 0; + } + } + else if (dlclose (handle) != 0) + { + fprintf (stderr, + "%s@LM_ERROR@ dlclose() failed : %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), strerror (errno)); + status = 1; + } + } + else + { + fprintf (stderr, + "%s@LM_ERROR@ ACE_ROOT environment variable not set\n", + time_stamp (tbuf, BUFSIZ, 'T')); + status = 1; + } + + fflush (stdout); + fflush (stderr); + fflush (logfp); + + fclose (logfp); + } + else + { + // Couldn't go into the log file !!! + printf ("%s@LM_DEBUG@ Starting %s test at %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), + program, time_stamp (ybuf, BUFSIZ, 'Y')); + + fprintf (stderr, + "%s@LM_ERROR@ Could not open log/UnloadLibACE.log : %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), strerror (errno)); + status = 1; + } + + printf ("%s@LM_DEBUG@ Ending %s test at %s\n", + time_stamp (tbuf, BUFSIZ, 'T'), + program, time_stamp (ybuf, BUFSIZ, 'Y')); + + fflush (stderr); + fflush (stdout); + fclose (stdout); + fclose (stderr); + + // Don't change this since we do NOT want to use ACE for this test! + exit (status); + return 0; +} +#else +# if defined (WIN32) && defined (ACE_USES_WCHAR) +// Borrow include list from ace_wchar.h +# if defined (ACE_HAS_WINCE) +# include /**/ <wtypes.h> +# elif !defined (__BORLANDC__) +# include /**/ <wchar.h> +# endif /* ACE_HAS_WINCE || __BORLANDC__ */ + +int +wmain (int, wchar_t **) +#else +int +main (int, char **) +#endif /* (WIN32) && (ACE_USES_WCHAR) */ +{ + char const *const program = "UnloadLibACE"; + + FILE *logfp = 0; + + if ((logfp = fopen ("log/UnloadLibACE.log", "w")) != 0) + { + fprintf (logfp, "@LM_DEBUG@ Starting %s test\n", program); + fprintf (logfp, "@LM_DEBUG@ %s test not implemented for this platform\n", + program); + fprintf (logfp, "@LM_DEBUG@ Ending %s test\n", program); + + fflush (logfp); + fclose (logfp); + } + return 0; +} +#endif /* UNLOAD_LIBACE_TEST */ diff --git a/ACE/tests/Upgradable_RW_Test.cpp b/ACE/tests/Upgradable_RW_Test.cpp new file mode 100644 index 00000000000..6a717279e1f --- /dev/null +++ b/ACE/tests/Upgradable_RW_Test.cpp @@ -0,0 +1,491 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Upgradable_RW_Test.cpp +// +// = DESCRIPTION +// This test program verifies the functionality of the ACE_OS +// implementation of readers/writer locks on Win32 and Posix +// pthreads. Use the RW_Mutex define switch to use +// readers/writer mutexes or regular mutexes. +// +// = AUTHOR +// Michael Kircher <mk1@cs.wustl.edu> +// +// ============================================================================ + +#include "Upgradable_RW_Test.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Atomic_Op.h" + +ACE_RCSID(tests, Upgradable_RW_Test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +// Default number of iterations. +static int n_iterations = 50; + +// Maximum string length used +static const size_t MAX_STRING_SIZE = 200; + +// switch on RW mutexes, else use ordinary mutexes +// #define RW_MUTEX 1 + +// Default number of readers. +static u_int n_readers = 10; + +// Default number of writers. +static u_int n_writers = 0; + +// Number of entries in the hash map +static u_int n_entries = 10; + +// Try to upgrade to a write lock, by default don't try. +static u_int use_try_upgrade = 0; + +// number of readers, which were able to upgrade +static u_int upgraded = 0; + +// count the number of find calls +static u_int find_called = 0; + +// number of readers, failing or not allowed to upgrade +static u_int not_upgraded = 0; + +// Thread creation flags. +static long thr_flags = THR_NEW_LWP; + +// Lock for shared_data (upgraded, not_upgraded, hash_Map) +#if defined (RW_MUTEX) +static ACE_RW_Thread_Mutex rw_mutex; +#else +static ACE_Thread_Mutex mutex; +#endif /* RW_MUTEX */ + +// Count of the number of readers and writers. +static ACE_Atomic_Op<ACE_Thread_Mutex, int> current_readers; +static ACE_Atomic_Op<ACE_Thread_Mutex, int> current_writers; + +static Linked_List *linked_list_ptr; + +// Returns 1 if found, +// 0 if not found, +// -1 on an error +static int +find_last (void) +{ + find_called++; + + char search_string[MAX_STRING_SIZE]; + ACE_OS::sprintf (search_string, + "%d", + n_entries - 1); + ACE_CString cString (search_string); + Element *element_ptr = 0; + + for (ACE_Double_Linked_List_Iterator<Element> iterator (*linked_list_ptr); + !iterator.done (); + iterator.advance ()) + { + element_ptr = iterator.next (); + if (element_ptr) + if (*element_ptr->value () == cString) + return 1; + } + + return 0; + +} + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("usage: %n [-r n_readers] [-w n_writers]\n") + ACE_TEXT (" [-e max_entries] [-u try update] ") + ACE_TEXT ("[-n iteration_count] [-f for FIFO threads]\n"))); + ACE_OS::exit (1); +} + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("e:fr:w:n:u")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'e': + n_entries = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'f': + thr_flags = THR_BOUND | THR_SCHED_FIFO; + break; + case 'r': + n_readers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'w': + n_writers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': + n_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'u': + use_try_upgrade = 1; + break; + default: + print_usage_and_die (); + break; + } +} + +// Iterate <n_iterations> each time checking that nobody modifies the data +// while we have a read lock. + +int +Reader_Task::svc (void) +{ + ACE_Profile_Timer timer; + ACE_Profile_Timer::ACE_Elapsed_Time elapsed_time; + + barrier_.wait (); + // Wait at the barrier. + + // We start an ACE_Profile_Timer here... + timer.start (); + + for (int iterations = 1; + iterations <= n_iterations; + iterations++) + { + ACE_Thread::yield (); + + int result = 0; + + { +#if defined (RW_MUTEX) + ACE_Read_Guard<ACE_RW_Thread_Mutex> g (rw_mutex); +#else + ACE_Guard<ACE_Thread_Mutex> g (mutex); +#endif /* RW_MUTEX */ + find_last (); +#if defined (RW_MUTEX) + if (use_try_upgrade) + result = + rw_mutex.tryacquire_write_upgrade (); +#endif /* RW_MUTEX */ + + // True, when we were able to upgrade. + if (result == 0 && use_try_upgrade) + { + //find_last (); try to find something which is not in + //there + upgraded++; + continue; + } + } + + if (result == -1 && errno == EBUSY // we tried and failed + || !use_try_upgrade) // we did not try at all + { +#if defined (RW_MUTEX) + ACE_Write_Guard<ACE_RW_Thread_Mutex> g (rw_mutex); +#else + ACE_Guard<ACE_Thread_Mutex> g (mutex); +#endif /* RW_MUTEX */ + + not_upgraded++; + find_last (); + } + else if (result == -1 && errno != EBUSY) + ACE_ERROR ((LM_ERROR, + ACE_TEXT (" (%t) failure in upgrading to write lock!\n"), + 1)); + } + + // Stop the timer. + timer.stop (); + timer.elapsed_time (elapsed_time); + + this->time_Calculation_.report_time (elapsed_time); + + return 0; +} + +// Iterate <n_iterations> each time modifying the global data and +// checking that nobody steps on it while we can write it. + +int +Writer_Task::svc (void) +{ + ACE_Profile_Timer timer; + ACE_Profile_Timer::ACE_Elapsed_Time elapsed_time; + + barrier_.wait (); + // Wait at the barrier + + // We start an ACE_Profile_Timer here... + timer.start (); + + for (int iterations = 1; + iterations <= n_iterations; + iterations++) + { + ACE_Thread::yield (); + +#if defined (RW_MUTEX) + ACE_Write_Guard<ACE_RW_Thread_Mutex> g (rw_mutex); +#else + ACE_Guard<ACE_Thread_Mutex> g (mutex); +#endif /* RW_MUTEX */ + + find_last (); + + current_writers--; + } + + // Stop the timer. + timer.stop (); + timer.elapsed_time (elapsed_time); + + this->time_Calculation_.report_time (elapsed_time); + + return 0; +} + +void +Time_Calculation::report_time (ACE_Profile_Timer::ACE_Elapsed_Time &elapsed_time) +{ + ACE_Guard<ACE_Thread_Mutex> g (mutex_); + + this->times_.real_time += elapsed_time.real_time; + this->times_.user_time += elapsed_time.user_time; + this->times_.system_time += elapsed_time.system_time; + + this->reported_times_++; +} + +void +Time_Calculation ::print_stats (void) +{ + ACE_Profile_Timer::ACE_Elapsed_Time elapsed_time = this->times_; + u_int iterations = 1; + + if (iterations > 0) + { + elapsed_time.real_time *= ACE_ONE_SECOND_IN_MSECS; + elapsed_time.user_time *= ACE_ONE_SECOND_IN_MSECS; + elapsed_time.system_time *= ACE_ONE_SECOND_IN_MSECS; + + elapsed_time.real_time /= iterations; + elapsed_time.user_time /= iterations; + elapsed_time.system_time /= iterations; + + double tmp = 0.0; + + if (elapsed_time.real_time != 0.0) + tmp = 1000 / elapsed_time.real_time; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n") + ACE_TEXT ("\treal_time\t = %0.06f ms, \n") + ACE_TEXT ("\tuser_time\t = %0.06f ms, \n") + ACE_TEXT ("\tsystem_time\t = %0.06f ms, \n") + ACE_TEXT ("\t%0.00f calls/second\n"), + elapsed_time.real_time < 0.0 ? 0.0 : elapsed_time.real_time, + elapsed_time.user_time < 0.0 ? 0.0 : elapsed_time.user_time, + elapsed_time.system_time < 0.0 ? 0.0 : elapsed_time.system_time, + tmp < 0.0 ? 0.0 : tmp)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Number of reported times: %d\n"), + this->reported_times_)); + } + else + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("\tNo time stats printed. Zero iterations or error ocurred.\n"))); +} + +static int +init (void) +{ + char entry[MAX_STRING_SIZE]; + ACE_CString *cString_ptr = 0; + Element *element_ptr = 0; + + ACE_NEW_RETURN (linked_list_ptr, + Linked_List, + -1); + + for (u_int i = 0; i < n_entries; i++) + { + ACE_OS::sprintf (entry, "%d", i); + ACE_NEW_RETURN (cString_ptr, + ACE_CString (entry), + -1); + ACE_NEW_RETURN (element_ptr, + Element (cString_ptr), + -1); + linked_list_ptr->insert_tail (element_ptr); + } + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +// Spawn off threads. + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("Upgradable_RW_Test")); + int status = 0; + +#if defined (ACE_HAS_THREADS) + parse_args (argc, argv); +#if !defined (RW_MUTEX) + use_try_upgrade = 0; + // make sure that we have to acquire the write lock +#endif /* RW_MUTEX */ + + current_readers = 0; // Possibly already done + current_writers = 0; // Possibly already done + + init (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) main thread starting\n"))); + + Time_Calculation time_Calculation; + // for the time calculation + + ACE_Barrier thread_barrier (n_readers + n_writers); + // for a nice start of all threads (for much contention) + + // Initialize the readers. + Reader_Task **reader_tasks = 0; + + ACE_NEW_RETURN (reader_tasks, + Reader_Task *[n_readers], + -1); + u_int i = 0; + + for (i = 0; + i < n_readers; + i++) + { + ACE_NEW_RETURN (reader_tasks[i], + Reader_Task (time_Calculation, + thread_barrier), + -1); + + reader_tasks[i]->activate (thr_flags, + 1, + 0, + ACE_DEFAULT_THREAD_PRIORITY); + } + + // Create all the writers + Writer_Task **writer_tasks = 0; + + ACE_NEW_RETURN (writer_tasks, + Writer_Task *[n_writers], + -1); + + for (i = 0; + i < n_writers; + i++) + { + ACE_NEW_RETURN (writer_tasks[i], + Writer_Task (time_Calculation, + thread_barrier), + -1); + + writer_tasks[i]->activate (thr_flags, + 1, + 0, + ACE_DEFAULT_THREAD_PRIORITY); + } + + // Wait a maximum of 1 second per iteration. + const ACE_Time_Value max_wait (n_iterations * 1); + const ACE_Time_Value wait_time (ACE_OS::gettimeofday () + max_wait); + if (ACE_Thread_Manager::instance ()->wait (&wait_time) == -1) + { + if (errno == ETIME) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("maximum wait time of %d msec exceeded\n"), + max_wait.msec ())); + else + ACE_OS::perror (ACE_TEXT ("wait")); + + status = -1; + } + + // compute average time. + time_Calculation.print_stats (); + + if (not_upgraded != 0 || upgraded != 0) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("upgraded to not upgraded ratio = %f \n"), + (float) upgraded / (float) (not_upgraded + upgraded))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Number of times, that find was called: %d\n"), + find_called)); + + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%t) exiting main thread\n"))); + + // Delete the memory of the Double_Linked_List + ACE_CString *cString_ptr = 0; + Element *element_ptr = 0; + + for (i = 0; + i < n_entries; + i++) + { + if (0 != (element_ptr = linked_list_ptr->delete_head ())) + { + cString_ptr = element_ptr->value (); + delete cString_ptr; + delete element_ptr; + } + } + + delete linked_list_ptr; + + for (i = 0; + i < n_writers; + i++) + delete writer_tasks[i]; + + delete [] writer_tasks; + + for (i = 0; + i < n_readers; + i++) + delete reader_tasks [i]; + + delete [] reader_tasks; +#else + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_INFO, + ACE_TEXT ("threads not supported on this platform\n"))); +#endif /* ACE_HAS_THREADS */ + + ACE_END_TEST; + return status; +} + diff --git a/ACE/tests/Upgradable_RW_Test.h b/ACE/tests/Upgradable_RW_Test.h new file mode 100644 index 00000000000..41c6c0dba1e --- /dev/null +++ b/ACE/tests/Upgradable_RW_Test.h @@ -0,0 +1,147 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Upgradable_RW_Test.h +// +// = DESCRIPTION +// This class gets its own header file to work around AIX C++ +// compiler "features" related to template instantiation... It is +// only used by Upgradable_RW_Test.cpp. +// +// = AUTHOR +// Michael Kircher <mk1@cs.wustl.edu> +// +// ============================================================================ + +#ifndef ACE_TESTS_UPGRADABLE_RW_TEST_H +#define ACE_TESTS_UPGRADABLE_RW_TEST_H + +#include "test_config.h" +#include "ace/Barrier.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Task.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" +#include "ace/Get_Opt.h" +#include "ace/SString.h" +#include "ace/Profile_Timer.h" + +class Element; + +class Element +{ + // = TITLE + // The members for the double linked list. + friend class ACE_Double_Linked_List<Element>; + friend class ACE_Double_Linked_List_Iterator_Base<Element>; + friend class ACE_Double_Linked_List_Iterator<Element>; + friend class ACE_Double_Linked_List_Reverse_Iterator<Element>; + +public: + Element (ACE_CString *item = 0, + Element *p = 0, + Element *n = 0) + : prev_ (p), + next_(n), + item_(item) + { + } + + ACE_CString *value (void) + { + return this->item_; + } + +private: + Element *prev_; + Element *next_; + ACE_CString *item_; +}; + +typedef ACE_Double_Linked_List<Element> Linked_List; + +class Time_Calculation +{ + // = TITLE + // class to do time calculations thread safe +public: + Time_Calculation (void) + : reported_times_ (0) + { + times_.real_time = 0; + times_.user_time = 0; + times_.system_time = 0; + } + + void report_time (ACE_Profile_Timer::ACE_Elapsed_Time &elapsed_time); + // take the time of the thread and add it to + + void print_stats (void); + +private: + ACE_Profile_Timer::ACE_Elapsed_Time times_; + // add the times incrementally + + ACE_SYNCH_MUTEX mutex_; + // protect the time + + unsigned int reported_times_; + // count how many threads gave me the elapsed_time +}; + +class Reader_Task : public ACE_Task_Base +{ + // = TITLE + // A Task for readers +public: + Reader_Task (Time_Calculation &time_Calculation, + ACE_Barrier &barrier) + : time_Calculation_ (time_Calculation), + barrier_(barrier) + { + }; + + virtual int svc (void); + +private: + Time_Calculation &time_Calculation_; + // keep a reference to the time calculation class + + ACE_Barrier &barrier_; + // keep this reference for the barrier, in order + // to allow a "nice" start +}; + +class Writer_Task : public ACE_Task_Base +{ + // = TITLE + // A Task for wirters. +public: + Writer_Task (Time_Calculation &time_Calculation, + ACE_Barrier &barrier) + : time_Calculation_ (time_Calculation), + barrier_(barrier) + { + }; + + virtual int svc (void); + +private: + Time_Calculation &time_Calculation_; + // keep a reference to the time calculation class + + ACE_Barrier &barrier_; + // keep this reference for the barrier, in order + // to allow a "nice" start +}; + +#endif /* ACE_TESTS_UPGRADABLE_RW_TEST_H */ diff --git a/ACE/tests/Vector_Test.cpp b/ACE/tests/Vector_Test.cpp new file mode 100644 index 00000000000..3866094c62b --- /dev/null +++ b/ACE/tests/Vector_Test.cpp @@ -0,0 +1,179 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// Vector_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the ACE_Vector class and its iterators. +// +// = AUTHOR +// Gonzalo A. Diethelm <gonzalo.diethelm@aditiva.com> and +// Karl-Heinz Wind <wind@itq.de> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID(tests, Vector_Test, "$Id$") + +#include "ace/Vector_T.h" + +typedef size_t DATA; +#if defined (__BORLANDC__) && (__BORLANDC__ <= 0x570) +// Borland C++ Builder 6 and earlier don't handle the second template +// argument correctly. We have to pass it explicitly +typedef ACE_Vector<DATA, ACE_VECTOR_DEFAULT_SIZE> VECTOR; +typedef ACE_Vector<DATA, ACE_VECTOR_DEFAULT_SIZE>::Iterator ITERATOR; +#else +typedef ACE_Vector<DATA> VECTOR; +typedef ACE_Vector<DATA>::Iterator ITERATOR; +#endif + +const size_t TOP = 100; +const size_t LEFT = 10; +const size_t RESIZE = 20; + +const size_t FILLER1 = 1; +const size_t FILLER2 = 2; + +int run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Vector_Test")); + + VECTOR vector; + size_t i; + + for (i = 0; i < TOP; ++i) + vector.push_back (i); + + ACE_ASSERT (vector.size () == TOP); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Size: %d\n"), + vector.size ())); + + for (i = 0; i < TOP; ++i) + ACE_ASSERT (vector[i] == i); + + // Test to be sure the iterator gets the correct count and entries. + ITERATOR iter (vector); + DATA *p_item = 0 ; + size_t iter_count = 0; + while (!iter.done ()) + { + if (iter.next (p_item) == 0) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Fail to get value on iter pass %d\n"), + iter_count)); + if (*p_item != iter_count) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Iter pass %d got %d\n"), + iter_count, *p_item)); + iter_count++; + iter.advance(); + } + if (iter_count != TOP) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Iterated %d elements; expected %d\n"), + iter_count, TOP)); + + for (i = 0; i < (TOP - LEFT); ++i) + vector.pop_back (); + + ACE_ASSERT (vector.size () == LEFT); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Size: %d\n"), + vector.size ())); + + for (i = 0; i < LEFT; ++i) + { + ACE_ASSERT (vector[i] == i); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("vector[%d]:%d\n"), + i, vector[i])); + } + + vector.resize(RESIZE, 0); + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("After resize\n"))); + + for (i = 0; i < RESIZE ; ++i) + { + // The original vector of size LEFT must have the same original contents + // the new elements should have the value 0 (this value is passed as + // second argument of the resize() call. + if (i < LEFT) + { + ACE_ASSERT (vector[i] == i); + } + else + { + ACE_ASSERT (vector[i] == 0); + } + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("vector[%d]:%d\n"), + i, vector[i])); + } + + vector.clear (); + ACE_ASSERT (vector.size () == 0); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Size: %d\n"), + vector.size ())); + + // test resize (shrink and enlarge with buffer realloc) + VECTOR vector2; + + // should be around 32 + size_t boundary = vector2.capacity (); + + // we fill everything up with 1 + // 1, 1, 1, 1, 1, 1, 1, 1, + // 1, 1, 1, 1, 1, 1, 1, 1, + // 1, 1, 1, 1, 1, 1, 1, 1, + // 1, 1, 1, 1, 1, 1, 1, 1, + for (i = 0; i < boundary; ++i) + vector2.push_back (FILLER1); + + // we throw almost everything away. + vector2.resize (1, 0); + + // we fill up with another pattern + // 1, 2, 2, 2, 2, 2, 2, 2, + // 2, 2, 2, 2, 2, 2, 2, 2, + // 2, 2, 2, 2, 2, 2, 2, 2, + // 2, 2, 2, 2, 2, 2, 2, 2, + // 2, + for (i = 0; i < boundary; ++i) + vector2.push_back (FILLER2); + + // now we check the result + ACE_ASSERT (vector2[0] == FILLER1); + for (i = 0; i < boundary; ++i) + ACE_ASSERT (vector2[i+1] == FILLER2); + + VECTOR v1; + VECTOR v2; + v1.push_back (1); + v2.push_back (1); + v1.push_back (2); + v2.push_back (2); + if (v1 != v2) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Inequality test failed!\n"))); + if (!(v1 == v2)) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("Equality test failed!\n"))); + + v1.push_back (3); + if (v1.size () != 3) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("v1's size should be 3\n"))); + + v1.swap (v2); + if (v2.size () != 3) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("v2's size should be 3\n"))); + + ACE_END_TEST; + + return 0; +} + + diff --git a/ACE/tests/WFMO_Reactor_Test.cpp b/ACE/tests/WFMO_Reactor_Test.cpp new file mode 100644 index 00000000000..2caf3a0e766 --- /dev/null +++ b/ACE/tests/WFMO_Reactor_Test.cpp @@ -0,0 +1,157 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// WFMO_Reactor_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the WFMO_Reactor. It makes sure that +// removals and suspensions work correctly. +// +// = AUTHOR +// Irfan Pyarali <irfan@oomworks.com> +// +// ============================================================================ + +#include "tests/test_config.h" +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Pipe.h" + +ACE_RCSID(tests, WFMO_Reactor_Test, "$Id$") + +#if defined (ACE_WIN32) + +static int number_of_handlers = 6; +static int number_of_closes = 0; + +class Event_Handler : public ACE_Event_Handler +{ +public: + + Event_Handler (ACE_Reactor &reactor); + + ~Event_Handler (void); + + ACE_Pipe pipe_; + +}; + +Event_Handler::Event_Handler (ACE_Reactor &reactor) +{ + this->reference_counting_policy ().value + (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + ACE_DEBUG ((LM_DEBUG, + "Reference count in Event_Handler() is %d\n", + this->reference_count_.value ())); + + this->reactor (&reactor); + + int result = + this->pipe_.open (); + + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + this->reactor ()->register_handler (this->pipe_.read_handle (), + this, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + this->reactor ()->register_handler (this->pipe_.write_handle (), + this, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); +} + +Event_Handler::~Event_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, + "Reference count in ~Event_Handler() is %d\n", + this->reference_count_.value ())); + + ++number_of_closes; +} + +void +test (void) +{ + int result = 0; + int i = 0; + + ACE_Reactor reactor (new ACE_WFMO_Reactor, 1); + + ACE_Event_Handler_var *safe_event_handlers = + new ACE_Event_Handler_var[number_of_handlers]; + + Event_Handler **event_handlers = + new Event_Handler*[number_of_handlers]; + + for (i = 0; i < number_of_handlers; ++i) + { + event_handlers[i] = + new Event_Handler (reactor); + + safe_event_handlers[i] = + event_handlers[i]; + } + + ACE_Time_Value timeout (0, 500 * 1000); + + result = reactor.run_reactor_event_loop (timeout); + ACE_ASSERT (result != -1); + + for (i = 0; i < number_of_handlers; ++i) + { + if (i % 2 == 0) + continue; + + result = reactor.suspend_handler (event_handlers[i]->pipe_.read_handle ()); + ACE_ASSERT (result == 0); + + result = reactor.suspend_handler (event_handlers[i]->pipe_.write_handle ()); + ACE_ASSERT (result == 0); + } + + result = reactor.run_reactor_event_loop (timeout); + ACE_ASSERT (result != -1); + + delete[] safe_event_handlers; + delete[] event_handlers; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("WFMO_Reactor_Test")); + + test (); + + ACE_ASSERT (number_of_closes == number_of_handlers); + + ACE_END_TEST; + + return 0; +} + +#else /* ACE_WIN32 */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("WFMO_Reactor_Test")); + + ACE_ERROR ((LM_INFO, + ACE_TEXT ("WFMO_Reactor not supported on this platform\n"))); + + ACE_END_TEST; + + return 0; +} + +#endif /* ACE_WIN32 */ diff --git a/ACE/tests/Win32clerk.conf b/ACE/tests/Win32clerk.conf new file mode 100644 index 00000000000..470f9b31b34 --- /dev/null +++ b/ACE/tests/Win32clerk.conf @@ -0,0 +1,3 @@ +# Note: hostname and port number need to be concatenated separated by ":" +dynamic Logging_Strategy Service_Object * ACE:_make_ACE_Logging_Strategy() "-s c:\temp\log\Time_Client.log -f OSTREAM" +dynamic Time_Server_Test Service_Object * netsvcs:_make_ACE_TS_Clerk_Processor () "-h localhost:10222 -t 4" diff --git a/ACE/tests/Win32server.conf b/ACE/tests/Win32server.conf new file mode 100644 index 00000000000..94a3b3906ec --- /dev/null +++ b/ACE/tests/Win32server.conf @@ -0,0 +1,10 @@ +# These are the services that can be linked into ACE. +# Note that you can replace the hardcoded "../lib/libnetsvcs.so" with +# a relative path if you set your LD search path correctly -- ACE will +# locate this for you automatically by reading your LD search path! +# In addition, you can replace the hardcoded "-p 20xxx" with "-p +# $PORTxxx" if you set your environment variables correctly. + +dynamic Logging_Strategy Service_Object * ACE:_make_ACE_Logging_Strategy() "-s c:\temp\log\Time_Server.log -f OSTREAM" +dynamic Time_Service Service_Object * netsvcs:_make_ACE_TS_Server_Acceptor() "-p 10222" + diff --git a/ACE/tests/Win32tokens.conf b/ACE/tests/Win32tokens.conf new file mode 100644 index 00000000000..e31eb04fc4d --- /dev/null +++ b/ACE/tests/Win32tokens.conf @@ -0,0 +1,4 @@ +# NT version +# +dynamic Logging_Strategy Service_Object * ACE:_make_ACE_Logging_Strategy() "-s c:\temp\log\Tokens_Test_Server.log -f OSTREAM" +dynamic Token_Service Service_Object * netsvcs:_make_ACE_Token_Acceptor() "-p 23456" diff --git a/ACE/tests/XtAthenaReactor_Test.cpp b/ACE/tests/XtAthenaReactor_Test.cpp new file mode 100644 index 00000000000..3bba4a52a6c --- /dev/null +++ b/ACE/tests/XtAthenaReactor_Test.cpp @@ -0,0 +1,322 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// XtReactor_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the possibility to integrate +// ACE to the X Main Loop. This program uses ACE_XtReactor class to +// schedule three additional event sources: +// 1. Events from button "Stop Test" (registed with XtAddCallback) +// 2. Events from button "Press Me" (registed with XtAddCallback) +// 3. Events from X timer (registed with XtAppAddTimeOut) +// 4. Events from ACE timer (registed with ACE_XtReactor::schedule_timer) +// 5. Events from the TCP/IP channel using ACE_Acceptor +// No command line arguments are needed to run the test. +// Programs needs Athena Widgets to be compiled and run. +// +// = AUTHOR +// Kirill Rybaltchenko <Kirill.Rybaltchenko@cern.ch> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID (tests, + XtAthenaReactor_Test, + "$Id$") + +#include "ace/XtReactor/XtReactor.h" +#include "ace/Event_Handler.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Service_Config.h" +#include "ace/Thread_Manager.h" + +#include "ace/OS_NS_unistd.h" + +#include /**/ <X11/Intrinsic.h> +#include /**/ <X11/Xatom.h> +#include /**/ <X11/Shell.h> + +#include /**/ <X11/Xaw/Command.h> +#include /**/ <X11/Xaw/Label.h> +#include /**/ <X11/Xaw/Box.h> +#include /**/ <X11/StringDefs.h> + +static void set_label(Widget w, const char *p) +{ + XtVaSetValues (w, XtNlabel, p, 0); +} +#define LABEL_WIDGET labelWidgetClass +#define BUTTON_WIDGET commandWidgetClass +#define PRESS_ME_CALLBACK XtNcallback +static Widget create_box(Widget parent, const char * name) +{ + return XtCreateWidget( (char*) name, boxWidgetClass, parent, 0, 0); +} + +// Port we listen on. +static const u_short SERV_TCP_PORT = 6670; + +// counter for events from "Press Me" button. +static int count1 = 0; + +// counter for events from X Timer. +static int count2 = 0; + +// counter for events from ACE Timer. +static int count3 = 0; + +// Callback for "Stop Test" buton - quit the program. +void +Quit (Widget, XtPointer, XtPointer) +{ + ACE_OS::exit (0); +} + +static void * +client (void *) +{ + char buf[100]; + size_t mes_len; + ACE_OS::sleep (1); + + ACE_DEBUG ((LM_DEBUG, + " (%P) Client: Starting...\n")); + + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connector; + ACE_OS::sprintf (buf, "Client: the life was good!"); + + mes_len = (int) htonl (ACE_OS::strlen (buf) + 1); + + if (connector.connect (stream, + ACE_INET_Addr (SERV_TCP_PORT, + ACE_DEFAULT_SERVER_HOST)) == -1) + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket open")); + + if (stream.send (4, + (void *) &mes_len, + sizeof (size_t), + (void *)buf, + ACE_OS::strlen (buf) + 1) == -1) + + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket send")); + + if (stream.close () == -1) + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket close")); + + ACE_DEBUG ((LM_DEBUG, + "(%P) Client: Message has been sent, about to exit...\n")); + return 0; +} + +// Callback for "Press Me" button. + +static void +inc_count (Widget, XtPointer client_data, XtPointer) +{ + char new_string[80]; + + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1++, + count2, + count3); + set_label((Widget) client_data, new_string); +} + +// Callback for X Timer. + +static void +inc_tmo (void *w,XtIntervalId *) +{ + char new_string[80]; + + if (count2 > 10) + ACE_OS::exit (0); + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1, + count2++, + count3); + + set_label((Widget) w, new_string); + + (void) XtAppAddTimeOut (XtWidgetToApplicationContext ((Widget) w), + 1000, + inc_tmo, + (Widget) w); +} + +class EV_handler : public ACE_Event_Handler +{ +public: + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + char new_string[80]; + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1, + count2, + count3++); + set_label((Widget) arg, new_string); + return 0; + } +}; + +class Connection_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void *) + { + //FUZZ: enable check_for_lack_ACE_OS + char buf[100]; + int head; + ssize_t ret = this->peer ().recv_n ((char *) &head, + sizeof (int)); + if (ret != sizeof (int)) + ACE_ERROR_RETURN ((LM_ERROR, + "(%P) %p\n", + "read header"), + -1); + + ret = this->peer ().recv_n (buf, + (int) ntohl (head)); + + if (ret != (int) ntohl (head)) + ACE_ERROR_RETURN ((LM_ERROR, + "(%P) %p\n", + "read message"), + -1); + ACE_DEBUG ((LM_DEBUG, + " (%P)Server (ACE_SOCKET channel message): [%s]\n", + buf)); + return 0; + } +}; + +#if defined (HummingBird_X) +extern "C" void HCLXmInit (void); +#endif /* HummingBird_X */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("XtAthenaReactor_Test")); + + XtAppContext app_context; + Widget topLevel, goodbye, PressMe, lbl, digits_rc; + Widget children[5]; + +#if defined (HummingBird_X) + HCLXmInit (); +#endif /* HummingBird_X */ + topLevel = XtVaAppInitialize (&app_context, + "XTReactor_Test", + 0, + 0, + &argc, + argv, + 0, + 0); + + digits_rc = create_box(topLevel, "digits_rc"); + + //"Stop Test" button. + goodbye = XtCreateWidget ( (char *) "goodbye", + BUTTON_WIDGET, + digits_rc, + 0, + 0); + set_label(goodbye, "Stop Test"); + + //"Press Me" button + PressMe = XtCreateWidget ((char *) "PressMe", + BUTTON_WIDGET, + digits_rc, + 0, + 0); + + //Display for event counter + lbl = XtCreateWidget ((char *) "label_for_event_one", + LABEL_WIDGET, + digits_rc, + 0, + 0); + set_label(lbl, "label_for_all_events"); + int ac = 0; + children[ac++] = goodbye; + children[ac++] = PressMe; + children[ac++] = lbl; + XtManageChildren (children, ac); + XtManageChild (digits_rc); + + //Register callback for "Stop Test" button + XtAddCallback (goodbye, PRESS_ME_CALLBACK, Quit, 0); + + //Register callback for "Press Me" button + XtAddCallback (PressMe, + PRESS_ME_CALLBACK, + inc_count, + (XtPointer) lbl); + + // Register callback for X Timer + (void) XtAppAddTimeOut (app_context, + 1000, + inc_tmo, + (XtPointer) lbl); + + XtRealizeWidget (topLevel); + + // It will perform X Main Loop + ACE_XtReactor reactor (app_context); + + ACE_Reactor r (&reactor); + + //Event Handler for ACE Timer. + EV_handler evh; + + ACE_Acceptor <Connection_Handler, ACE_SOCK_ACCEPTOR> acceptor; + + if (acceptor.open (ACE_INET_Addr ((u_short) SERV_TCP_PORT), + &r) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open"), + -1); + + if (reactor.schedule_timer (&evh, + (const void *) lbl, + ACE_Time_Value (2), + ACE_Time_Value (2))==-1) + ACE_ERROR_RETURN ((LM_ERROR, + " (%P|%t) can't register with reactor\n"), + -1); + + ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) client, + 0, + THR_NEW_LWP | THR_DETACHED); + + XtAppMainLoop (XtWidgetToApplicationContext (topLevel)); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/XtMotifReactor_Test.cpp b/ACE/tests/XtMotifReactor_Test.cpp new file mode 100644 index 00000000000..bbfeecfe998 --- /dev/null +++ b/ACE/tests/XtMotifReactor_Test.cpp @@ -0,0 +1,329 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// XtReactor_Test.cpp +// +// = DESCRIPTION +// This is a simple test that illustrates the possibility to integrate +// ACE to the X Main Loop. This program uses ACE_XtReactor class to +// schedule three additional event sources: +// 1. Events from button "Stop Test" (registed with XtAddCallback) +// 2. Events from button "Press Me" (registed with XtAddCallback) +// 3. Events from X timer (registed with XtAppAddTimeOut) +// 4. Events from ACE timer (registed with ACE_XtReactor::schedule_timer) +// 5. Events from the TCP/IP channel using ACE_Acceptor +// No command line arguments are needed to run the test. +// The program needs Motif or lestiff to be compiled and run. +// +// = AUTHOR +// Kirill Rybaltchenko <Kirill.Rybaltchenko@cern.ch> +// +// ============================================================================ + +#include "test_config.h" + +ACE_RCSID (tests, + XtMotifReactor_Test, + "$Id$") +#include "ace/XtReactor/XtReactor.h" +#include "ace/Event_Handler.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/Service_Config.h" +#include "ace/Thread_Manager.h" + +#include "ace/OS_NS_unistd.h" + +#include /**/ <X11/Intrinsic.h> +#include /**/ <X11/Xatom.h> +#include /**/ <X11/Shell.h> + +#include /**/ <Xm/Xm.h> +#include /**/ <Xm/Label.h> +#include /**/ <Xm/PushB.h> +#include /**/ <Xm/RowColumn.h> + +static void set_label(Widget w, const char *p) +{ + XtVaSetValues (w, + XmNlabelString, + XmStringCreateLocalized( (char*) p), + 0); +} +#define LABEL_WIDGET xmLabelWidgetClass +#define BUTTON_WIDGET xmPushButtonWidgetClass +#define PRESS_ME_CALLBACK XmNactivateCallback +static Widget create_box(Widget parent, const char *name) +{ + Arg al[10]; + int ac = 0; + XtSetArg (al[ac], XmNnumColumns, 3); ac++; + XtSetArg (al[ac], XmNpacking, XmPACK_COLUMN); ac++; + XtSetArg (al[ac], XmNentryAlignment, XmALIGNMENT_CENTER); ac++; + return XmCreateRowColumn (parent, (char *) name, al, ac); +} + +// Port we listen on. +static const u_short SERV_TCP_PORT = 6670; + +// counter for events from "Press Me" button. +static int count1 = 0; + +// counter for events from X Timer. +static int count2 = 0; + +// counter for events from ACE Timer. +static int count3 = 0; + +// Callback for "Stop Test" buton - quit the program. +void +Quit (Widget, XtPointer, XtPointer) +{ + ACE_OS::exit (0); +} + +static void * +client (void *) +{ + char buf[100]; + size_t mes_len; + ACE_OS::sleep (1); + + ACE_DEBUG ((LM_DEBUG, + " (%P) Client: Starting...\n")); + + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connector; + ACE_OS::sprintf (buf, "Client: the life was good!"); + + mes_len = (int) htonl (ACE_OS::strlen (buf) + 1); + + if (connector.connect (stream, + ACE_INET_Addr (SERV_TCP_PORT, + ACE_DEFAULT_SERVER_HOST)) == -1) + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket open")); + + if (stream.send (4, + (void *) &mes_len, + sizeof (size_t), + (void *)buf, + ACE_OS::strlen (buf) + 1) == -1) + + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket send")); + + if (stream.close () == -1) + ACE_ERROR ((LM_ERROR, + "(%P) %p\n", + "Socket close")); + + ACE_DEBUG ((LM_DEBUG, + "(%P) Client: Message has been sent, about to exit...\n")); + return 0; +} + +// Callback for "Press Me" button. + +static void +inc_count (Widget, XtPointer client_data, XtPointer) +{ + char new_string[80]; + + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1++, + count2, + count3); + set_label((Widget) client_data, new_string); +} + +// Callback for X Timer. + +static void +inc_tmo (void *w,XtIntervalId *) +{ + char new_string[80]; + + if (count2 > 10) + ACE_OS::exit (0); + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1, + count2++, + count3); + + set_label((Widget) w, new_string); + + (void) XtAppAddTimeOut (XtWidgetToApplicationContext ((Widget) w), + 1000, + inc_tmo, + (Widget) w); +} + +class EV_handler : public ACE_Event_Handler +{ +public: + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + char new_string[80]; + ACE_OS::sprintf (new_string, + "Events: [%d] [%d] [%d]", + count1, + count2, + count3++); + set_label((Widget) arg, new_string); + return 0; + } +}; + +class Connection_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + //FUZZ: disable check_for_lack_ACE_OS + virtual int open (void *) + { + //FUZZ: enable check_for_lack_ACE_OS + char buf[100]; + int head; + ssize_t ret = this->peer ().recv_n ((char *) &head, + sizeof (int)); + if (ret != sizeof (int)) + ACE_ERROR_RETURN ((LM_ERROR, + "(%P) %p\n", + "read header"), + -1); + + ret = this->peer ().recv_n (buf, + (int) ntohl (head)); + + if (ret != (int) ntohl (head)) + ACE_ERROR_RETURN ((LM_ERROR, + "(%P) %p\n", + "read message"), + -1); + ACE_DEBUG ((LM_DEBUG, + " (%P)Server (ACE_SOCKET channel message): [%s]\n", + buf)); + return 0; + } +}; + +#if defined (HummingBird_X) +extern "C" void HCLXmInit (void); +#endif /* HummingBird_X */ + +int +run_main (int argc, ACE_TCHAR *argv[]) +{ + ACE_START_TEST (ACE_TEXT ("XtMotifReactor_Test")); + + XtAppContext app_context; + Widget topLevel, goodbye, PressMe, lbl, digits_rc; + Widget children[5]; + +#if defined (HummingBird_X) + HCLXmInit (); +#endif /* HummingBird_X */ + topLevel = XtVaAppInitialize (&app_context, + "XTReactor_Test", + 0, + 0, + &argc, + argv, + 0, + 0); + + digits_rc = create_box(topLevel, "digits_rc"); + + //"Stop Test" button. + goodbye = XtCreateWidget ( (char *) "goodbye", + BUTTON_WIDGET, + digits_rc, + 0, + 0); + set_label(goodbye, "Stop Test"); + + //"Press Me" button + PressMe = XtCreateWidget ((char *) "PressMe", + BUTTON_WIDGET, + digits_rc, + 0, + 0); + + //Display for event counter + lbl = XtCreateWidget ((char *) "label_for_event_one", + LABEL_WIDGET, + digits_rc, + 0, + 0); + set_label(lbl, "label_for_all_events"); + int ac = 0; + children[ac++] = goodbye; + children[ac++] = PressMe; + children[ac++] = lbl; + XtManageChildren (children, ac); + XtManageChild (digits_rc); + + //Register callback for "Stop Test" button + XtAddCallback (goodbye, PRESS_ME_CALLBACK, Quit, 0); + + //Register callback for "Press Me" button + XtAddCallback (PressMe, + PRESS_ME_CALLBACK, + inc_count, + (XtPointer) lbl); + + // Register callback for X Timer + (void) XtAppAddTimeOut (app_context, + 1000, + inc_tmo, + (XtPointer) lbl); + + XtRealizeWidget (topLevel); + + // It will perform X Main Loop + ACE_XtReactor reactor (app_context); + + ACE_Reactor r (&reactor); + + //Event Handler for ACE Timer. + EV_handler evh; + + ACE_Acceptor <Connection_Handler, ACE_SOCK_ACCEPTOR> acceptor; + + if (acceptor.open (ACE_INET_Addr ((u_short) SERV_TCP_PORT), + &r) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open"), + -1); + + if (reactor.schedule_timer (&evh, + (const void *) lbl, + ACE_Time_Value (2), + ACE_Time_Value (2))==-1) + ACE_ERROR_RETURN ((LM_ERROR, + " (%P|%t) can't register with reactor\n"), + -1); + + ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC) client, + 0, + THR_NEW_LWP | THR_DETACHED); + + XtAppMainLoop (XtWidgetToApplicationContext (topLevel)); + + ACE_END_TEST; + return 0; +} + diff --git a/ACE/tests/acetest.mpb b/ACE/tests/acetest.mpb new file mode 100644 index 00000000000..f9c60d247fc --- /dev/null +++ b/ACE/tests/acetest.mpb @@ -0,0 +1,22 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + + after += Test_Output + libs += Test_Output + + Source_Files { + Main.cpp + } + Header_Files { + } + Resource_Files { + } + Documentation_Files { + } + Inline_Files { + } + Template_Files { + } +} diff --git a/ACE/tests/dll_test_parent_lib.mpb b/ACE/tests/dll_test_parent_lib.mpb new file mode 100644 index 00000000000..0949642442a --- /dev/null +++ b/ACE/tests/dll_test_parent_lib.mpb @@ -0,0 +1,10 @@ +// -*- MPC -*- +// +// $Id$ + +project { + + after += DLL_Test_Parent_Lib + libs += DLL_Test_Parent + +} diff --git a/ACE/tests/pharlap/run_pharlap_tests.bat b/ACE/tests/pharlap/run_pharlap_tests.bat new file mode 100755 index 00000000000..910f2a678f5 --- /dev/null +++ b/ACE/tests/pharlap/run_pharlap_tests.bat @@ -0,0 +1,130 @@ +@echo off +rem $Id$ + +rem This file runs all the tests on PharLap ETS. +rem To use this either give it no arguments to run all the tests or +rem pass it the test name (without the extention) to run only one +rem test + +setlocal + +set arg=%1 + +if not "%1" == "" goto runtest + +:runall + +rem call %0 ACE_Init_Test +call %0 Atomic_Op_Test +call %0 Barrier_Test +call %0 Basic_Types_Test +call %0 Buffer_Stream_Test +call %0 CDR_Test +call %0 Collection_Test +call %0 Conn_Test +call %0 Cached_Conn_Test +call %0 Cached_Accept_Conn_Test +call %0 DLL_Test +call %0 DLList_Test +call %0 Enum_Interfaces_Test +call %0 Env_Value_Test +call %0 Future_Test +call %0 Handle_Set_Test +call %0 Hash_Map_Manager_Test +call %0 Lazy_Map_Manager_Test +call %0 Hash_Map_Bucket_Iterator_Test +call %0 High_Res_Timer_Test +call %0 IOStream_Test +call %0 Map_Manager_Test +call %0 Cache_Map_Manager_Test +call %0 Map_Test +rem call %0 Mem_Map_Test not supported +call %0 Message_Block_Test +call %0 Message_Queue_Notifications_Test +call %0 Message_Queue_Test +call %0 MT_Reactor_Timer_Test +rem call %0 MM_Shared_Memory_Test not supported +call %0 MT_SOCK_Test +call %0 Naming_Test +call %0 New_Fail_Test +call %0 Notify_Performance_Test +call %0 Object_Manager_Test +call %0 OrdMultiSet_Test +call %0 Pipe_Test +call %0 Priority_Buffer_Test +call %0 Dynamic_Priority_Test +call %0 Priority_Reactor_Test +call %0 Priority_Task_Test +call %0 Process_Mutex_Test +call %0 Process_Strategy_Test +call %0 RB_Tree_Test +call %0 Reactors_Test +call %0 Reactor_Exceptions_Test +call %0 Reactor_Notify_Test +call %0 Reactor_Performance_Test +call %0 Reactor_Timer_Test +call %0 Reader_Writer_Test +call %0 Thread_Pool_Reactor_Test +call %0 Recursive_Mutex_Test +call %0 Reverse_Lock_Test +call %0 Semaphore_Test +call %0 Service_Config_Test +call %0 Sigset_Ops_Test +call %0 Simple_Message_Block_Test +call %0 Svc_Handler_Test +call %0 SOCK_Test +call %0 SOCK_Connector_Test +call %0 SOCK_Send_Recv_Test +rem call %0 SPIPE_Test not supported +call %0 SString_Test +call %0 SV_Shared_Memory_Test +call %0 Task_Test +call %0 Thread_Manager_Test +call %0 Thread_Mutex_Test +call %0 Thread_Pool_Test +call %0 Timer_Queue_Test +call %0 Timeprobe_Test +if exist ..\netsvcs\servers\main.exe call %0 Time_Service_Test +call %0 Time_Value_Test +call %0 Tokens_Test +call %0 TSS_Test +call %0 UPIPE_SAP_Test +call %0 Upgradable_RW_Test +goto done + +:runtest + +echo Running %arg% +if not exist %arg%_ETS.exe goto nofile + +RUNEMB -LOGHOST -NODIALOG %arg%_ETS.exe > log\%arg%.log +if errorlevel 0 goto fine +echo. +echo %arg% has FAILED!!! +echo. +type log\%arg%.log | find /I "Abnormal program termination" +type log/%arg%.log | find /I "target halted" +type log/%arg%.log | find /I "Fatal error" +type log\%arg%.log | find /I "assertion failed" +type log\%arg%.log | find /I "not supported" +type log\%arg%.log | find /I "no such file or directory" +type log\%arg%.log | find /I "invalid argument" +type log\%arg%.log | find /I "timeout" +type log\%arg%.log | find /I "bad file number" +type log\%arg%.log | find /I "Win32 structured exception" +echo. + +goto done + +:nofile +echo %arg%.exe not found +goto done + +:fine + +rem We should check the log files here to make sure the test ended correctly +rem type log\%arg%.log | find "Ending" + +:done + +endlocal diff --git a/ACE/tests/randomize.h b/ACE/tests/randomize.h new file mode 100644 index 00000000000..5a6b5ff3fcb --- /dev/null +++ b/ACE/tests/randomize.h @@ -0,0 +1,126 @@ +// -*- C++ -*- + +// ============================================================================ +/** + * @file randomize.h + * + * $Id$ + * + * Randomize the contents of an array. + * + * @author Ossama Othman + * + * @note Based on code originally found in test_config.h and Test_Output.cpp + */ +// ============================================================================ + +#ifndef ACE_TESTS_RANDOMIZE_H +#define ACE_TESTS_RANDOMIZE_H + +#include "ace/OS_NS_stdlib.h" + +#include <algorithm> +#include <functional> + +namespace +{ + /** + * @class randomize_index + * + * @brief Functor that randomoly swaps the order of two array + * elements. + * + * @internal Do not directly use this class. Use the @c randomize() + * function template found below instead. + * + * This functor generates random array index using high-order bits (e.g., + * MAX_VAL * rand() / (RAND_MAX + 1.0)) rather than low-order ones + * (e.g. rand() % MAX_VAL) to improve randomness. The array element + * reference by that index is then swapped with the array element + * provided to the functor. + * + * @note The improved randomness may be overkill for this functor's + * intended use cases, but it is a good thing to practice/have + * nonetheless. + * + * @see Numerical Recipes in C: The Art of Scientific Computing + * (William H. Press, Brian P. Flannery, Saul A. Teukolsky, + * William T. Vetterling; New York: Cambridge University Press, + * 1992 (2nd ed., p. 277). + */ + template<typename T> + class randomize_element + : public std::unary_function<T &, void> + { + public: + + randomize_element (T * array, size_t size, ACE_RANDR_TYPE seed) + : array_ (array) + , coefficient_ (static_cast<double> (size) / (RAND_MAX + 1.0f)) + , seed_ (seed) + { + } + + void + operator() (T & value) // We need this to be a reference! + { + size_t const index = + static_cast<size_t> ( + this->coefficient_ * ACE_OS::rand_r (this->seed_)); + + // Swap rather than assign so that we don't lose the original + // value. + std::swap (value, this->array_[index]); + } + + private: + + // The array. + T * const array_; + + // Factor out the constant coefficient. + double const coefficient_; + + // Random number generator seed value. + ACE_RANDR_TYPE seed_; + + }; + + /** + * @fn randomize + * + * @brief Randomize the order of elements in an array. + * + * @param array The array whose elements will be reordered + * randomly. + * @param size The number of elements in the array. + * @param seed The random number generator seed value. A default + * value is provided so that the generated random + number order may be "repeatable" if so desired. + * + * This function template randomizes the order of elements in an + * array of any type. It is also reentrant (unless the array itself + * is accessed by multiple threads). + * @par + * Sample usage: + * @par + * \verbatim + static size_t const NUM_TIMES = 100; + ACE_Time_Value times[NUM_TIMES] = { ... }; + randomize (times, + NUM_TIMES, + static_cast<ACE_RANDR_TYPE> (ACE_OS::time (0L))); + \endverbatim + */ + template<typename T> + void + randomize (T * array, size_t size, ACE_RANDR_TYPE seed = 0) + { + // Randomize all elements of the array. + std::for_each (array, + array + size, + randomize_element<T> (array, size, seed)); + } +} + +#endif /* !ACE_TESTS_RANDOMIZE_H */ diff --git a/ACE/tests/run_test.lst b/ACE/tests/run_test.lst new file mode 100644 index 00000000000..7a5c86217ad --- /dev/null +++ b/ACE/tests/run_test.lst @@ -0,0 +1,194 @@ +# $Id$ +# +# This is the list of tests that need to be run by run_test.pl. +# Each line has its own test, and a test can be followed by a +# list of platforms it runs or does not run on. +# +# Example: Foo_Test: !linux +# Example: Bar_Test: +# Example: Baz_Test: Win32 !Borland +# +# Foo_Test runs on all configurations except for linux +# +# Bar_Test runs on all configurations +# +# Baz_Test only runs on Win32 configurations but not on Borland +# configurations. + +ACE_Init_Test: MSVC +ACE_Test +Aio_Platform_Test +Arg_Shifter_Test +ARGV_Test +Array_Map_Test +Atomic_Op_Test +Auto_Event_Test +Auto_IncDec_Test +Barrier_Test +Based_Pointer_Test: !STATIC !VxWorks !ACE_FOR_TAO !PHARLAP +Basic_Types_Test +Bound_Ptr_Test: !ACE_FOR_TAO +Buffer_Stream_Test +Bug_1576_Regression_Test +Bug_1890_Regression_Test +Bug_2368_Regression_Test: ALL !DISABLED +Bug_2497_Regression_Test +Bug_2540_Regression_Test +#Bug_2609_Regression_Test: !ST !ACE_FOR_TAO +#Bug_2610_Regression_Test: !ST !ACE_FOR_TAO +Bug_2659_Regression_Test: !ST !VxWorks64 !VxWorks65 +Bug_2653_Regression_Test: !ST +Bug_2815_Regression_Test +Bug_2820_Regression_Test +Bug_3102_Regression_Test +CDR_Array_Test: !ACE_FOR_TAO +CDR_File_Test: !ACE_FOR_TAO +CDR_Test +Cache_Map_Manager_Test +Cached_Accept_Conn_Test: !VxWorks !ACE_FOR_TAO !LabVIEW_RT +Cached_Allocator_Test: !ACE_FOR_TAO +Cached_Conn_Test: !VxWorks !ACE_FOR_TAO !LabVIEW_RT +Capabilities_Test: !ACE_FOR_TAO +Codecs_Test: !NO_CODECS !ACE_FOR_TAO +Collection_Test +Config_Test: !LynxOS !VxWorks !ACE_FOR_TAO +Conn_Test: !ACE_FOR_TAO +DLL_Test: !Unicos !STATIC !KCC_Linux +DLList_Test: !ACE_FOR_TAO +Date_Time_Test: !ACE_FOR_TAO +Dev_Poll_Reactor_Test: !nsk +Dirent_Test: !VxWorks_RTP !LabVIEW_RT +Dynamic_Priority_Test +Dynamic_Test +Enum_Interfaces_Test: !LynxOS !NO_NETWORK +Env_Value_Test: !WinCE !LabVIEW_RT +FIFO_Test: !ACE_FOR_TAO +Framework_Component_Test: !STATIC !nsk +Future_Set_Test: !nsk !ACE_FOR_TAO +Future_Test: !nsk !ACE_FOR_TAO +Get_Opt_Test +Handle_Set_Test: !ACE_FOR_TAO +Hash_Map_Bucket_Iterator_Test +Hash_Map_Manager_Test +Hash_Multi_Map_Manager_Test +High_Res_Timer_Test: !ACE_FOR_TAO +INET_Addr_Test: !NO_NETWORK +IOStream_Test +Integer_Truncate_Test +Lazy_Map_Manager_Test +Log_Msg_Test: !ACE_FOR_TAO +Log_Msg_Backend_Test: !ACE_FOR_TAO +Logging_Strategy_Test: !LynxOS !STATIC !ST +Manual_Event_Test +MEM_Stream_Test: !VxWorks !nsk !ACE_FOR_TAO !PHARLAP +MM_Shared_Memory_Test: !Unicos !VxWorks !nsk !ACE_FOR_TAO +MT_Reactor_Timer_Test +MT_Reactor_Upcall_Test: !nsk +MT_Reference_Counted_Event_Handler_Test: !OpenVMS_IA64Crash +MT_Reference_Counted_Notify_Test +MT_SOCK_Test: !LynxOS +Malloc_Test: !VxWorks !LynxOS !ACE_FOR_TAO !PHARLAP +Map_Manager_Test: !ACE_FOR_TAO +Map_Test: !ACE_FOR_TAO +Max_Default_Port_Test: !ST +Mem_Map_Test: !Unicos !VxWorks !nsk !ACE_FOR_TAO +Memcpy_Test: !ACE_FOR_TAO +Message_Block_Test: !ACE_FOR_TAO +Message_Queue_Notifications_Test +Message_Queue_Test: !ACE_FOR_TAO +Message_Queue_Test_Ex: !ACE_FOR_TAO +Multicast_Test: !ST !NO_MCAST !nsk !LynxOS !LabVIEW_RT +Multihomed_INET_Addr_Test: !ACE_FOR_TAO +Naming_Test: !NO_OTHER !LynxOS !Unicos !VxWorks !nsk !ACE_FOR_TAO !PHARLAP +Network_Adapters_Test: !LynxOS +New_Fail_Test: ALL !DISABLED +NonBlocking_Conn_Test +Notification_Queue_Unit_Test +Notify_Performance_Test: !nsk !ACE_FOR_TAO +OS_Test +Object_Manager_Test +Object_Manager_Flipping_Test +Obstack_Test +OrdMultiSet_Test +Pipe_Test: !VxWorks !PHARLAP +Priority_Buffer_Test +Priority_Reactor_Test: !ACE_FOR_TAO +Priority_Task_Test: !Unicos +Proactor_Scatter_Gather_Test: !VxWorks !nsk !ACE_FOR_TAO +Proactor_Test: !VxWorks !LynxOS !nsk !ACE_FOR_TAO !BAD_AIO +Proactor_Timer_Test: !VxWorks !nsk !ACE_FOR_TAO +Process_Manager_Test: !VxWorks !ACE_FOR_TAO !PHARLAP +Process_Manual_Event_Test: !HPUX !VxWorks !ACE_FOR_TAO !PHARLAP +Process_Mutex_Test: !VxWorks !ACE_FOR_TAO !PHARLAP +Process_Semaphore_Test: !VxWorks !ACE_FOR_TAO !PHARLAP +RB_Tree_Test +Reactor_Dispatch_Order_Test +Reactor_Exceptions_Test +Reactor_Notify_Test: !ST !ACE_FOR_TAO +Reactor_Notification_Queue_Test +Reactor_Performance_Test: !ACE_FOR_TAO +Reactor_Registration_Test +Reactor_Remove_Resume_Test +Reactor_Timer_Test: !ACE_FOR_TAO +Reactors_Test +Reader_Writer_Test +Recursive_Condition_Test: !ST +Recursive_Mutex_Test: !ST +Refcounted_Auto_Ptr_Test: !ACE_FOR_TAO +Reference_Counted_Event_Handler_Test +Reverse_Lock_Test +Sendfile_Test: !QNX !NO_NETWORK !VxWorks +Signal_Test: !VxWorks64 !VxWorks65 !LynxOS +SOCK_Connector_Test: !NO_NETWORK +SOCK_Netlink_Test: !ACE_FOR_TAO +SOCK_Send_Recv_Test: !NO_NETWORK +SOCK_Test: !NO_NETWORK +SPIPE_Test: !VxWorks !nsk !ACE_FOR_TAO +SString_Test: !ACE_FOR_TAO +SV_Shared_Memory_Test: !MSVC !Unicos !VxWorks !RH_7.1 !nsk !ACE_FOR_TAO +Semaphore_Test: !ACE_FOR_TAO +Service_Config_Test: !STATIC +Sigset_Ops_Test +Simple_Message_Block_Test +Svc_Handler_Test: !ACE_FOR_TAO +TP_Reactor_Test: !LynxOS !ACE_FOR_TAO +TSS_Test +TSS_Static_Test +Task_Test +Task_Ex_Test +Thread_Manager_Test: !Unicos +Thread_Mutex_Test +Thread_Pool_Reactor_Resume_Test: !NO_OTHER !ST +Thread_Pool_Reactor_Test: !NO_OTHER +Thread_Pool_Test +Thread_Creation_Threshold_Test +Time_Service_Test: !STATIC !DISABLED !missing_netsvcs TOKEN !Unicos +Time_Value_Test +Timeprobe_Test +Timer_Cancellation_Test +Timer_Queue_Reference_Counting_Test +Timer_Queue_Test: !ACE_FOR_TAO +Token_Strategy_Test: !ST !nsk +Tokens_Test: MSVC !DISABLED TOKEN !Unicos +UPIPE_SAP_Test: !VxWorks !nsk !ACE_FOR_TAO +Unbounded_Set_Test +Upgradable_RW_Test: !ACE_FOR_TAO +Vector_Test +WFMO_Reactor_Test: !nsk +INET_Addr_Test_IPV6: !nsk +Max_Default_Port_Test_IPV6: !nsk +Multicast_Test_IPV6: !NO_MCAST !nsk +Multihomed_INET_Addr_Test_IPV6: !nsk !ACE_FOR_TAO +Proactor_Test_IPV6: !nsk !ACE_FOR_TAO !BAD_AIO +SOCK_Send_Recv_Test_IPV6 +SOCK_Dgram_Test: !NO_NETWORK +SOCK_Dgram_Bcast_Test: !LynxOS !ACE_FOR_TAO +SOCK_SEQPACK_SCTP_Test: !MSVC !nsk !ACE_FOR_TAO +SOCK_Test_IPv6: !nsk +Process_Strategy_Test: !VxWorks !LynxOS !ACE_FOR_TAO !PHARLAP +Recursive_Condition_Bug_Test: !ST +UnloadLibACE: !STATIC !WinCE !LabVIEW_RT +UUID_Test: !NO_UUID !ACE_FOR_TAO +SSL/Bug_2912_Regression_Test: SSL !ACE_FOR_TAO !BAD_AIO +SSL/SSL_Asynch_Stream_Test: SSL !ACE_FOR_TAO !BAD_AIO +SSL/Thread_Pool_Reactor_SSL_Test: SSL diff --git a/ACE/tests/run_test.pl b/ACE/tests/run_test.pl new file mode 100755 index 00000000000..f33caad39a2 --- /dev/null +++ b/ACE/tests/run_test.pl @@ -0,0 +1,569 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# -*- perl -*- +# This file is for running the tests in the ACE tests directory. +# It is usually used for auto_compiles. + +if (defined $ENV{ACE_ROOT}) { + use lib "$ENV{ACE_ROOT}/bin"; +} else { + use lib '../bin'; +} +if (defined $ENV{top_srcdir}) { + use lib "$ENV{top_srcdir}/bin"; +} + +use PerlACE::Run_Test; +use PerlACE::TestTarget; + +use Cwd; +use English; +use Getopt::Std; +use FileHandle; +use File::Basename; + +$config_list = new PerlACE::ConfigList; + +PerlACE::add_lib_path("$ENV{ACE_ROOT}/tests"); + +################################################################################ + +sub check_for_more_configs () +{ + my $fh = new FileHandle; + + if ($fh->open ("< ../ace/ACE_COMPONENTS.list")) { + while (<$fh>) { + if (m/ Other /) { + print "Adding 'Other' as my config\n" if defined $opt_d; + $config_list->add_one_config ('OTHER'); + } + if (m/ Token /) { + print "Adding 'Token' as my config\n" if defined $opt_d; + $config_list->add_one_config ('TOKEN'); + } + } + + $fh->close (); + } + elsif (defined $opt_d) { + print "Could not open ACE_COMPONENTS.list file\n" if defined $opt_d; + print "Assuming TOKEN and OTHER subsets are included\n" if defined $opt_d; + $config_list->add_one_config ('OTHER'); + $config_list->add_one_config ('TOKEN'); + } + + my $P = new PerlACE::Process ("../netsvcs/servers/main"); + + if (!-x $P->Executable ()) { + $config_list->add_one_config ('missing_netsvcs'); + } + + if (defined $opt_v) { + $config_list->add_one_config ('VxWorks'); + } +} + +################################################################################ + +sub record_resources () +{ + if ($config_list->check_config ('CHECK_RESOURCES')) { + if (!defined $ENV{'LOGNAME'}) { + $user=`whoami`; + } + else { + $user = $ENV{'LOGNAME'}; + } + + $start_test_resources=`ipcs | egrep $user`; + } +} + +################################################################################ + +sub check_resources +{ + my($oh) = shift; + if ($config_list->check_config ('CHECK_RESOURCES')) { + if (defined $opt_v) { + print $oh "memShow();\n"; + } + else { + $end_test_resources=`ipcs | egrep $user`; + + if ("$start_test_resources" ne "$end_test_resources") { + print STDERR "Warning: the ACE tests _may_ have leaked OS ". + "resources!\n"; + print STDERR "Warning: Before: $start_test_resources\n"; + print STDERR "Warning: After: $end_test_resources\n"; + } + } + } +} + +################################################################################ + +sub run_program ($) +{ + my $path = shift; + my $arguments = shift; + if ($path =~ /^(\S*)\s*(.*)/ ) { + $path = $1; + $arguments = $2 . $arguments; + } + + ## Print it out before we check for the executable + ## if the executable doesn't exist, the error will show + ## up as part of the previous test. + print "auto_run_tests: tests/$path $arguments\n"; + + my ($program, $dir, $suffix) = fileparse($path); + my $start_dir = getcwd (); + if ($dir ne "" && !chdir $dir) { + print STDERR "Error: Can\'t chdir to $dir for $path\n"; + return; + } + unlink <log/$program*.log>; + unlink "core"; + + my $P; + + if ($config_list->check_config ('Valgrind')) { + $P = new PerlACE::Process ($program,$arguments); + $P->IgnoreExeSubDir(1); + } + else { + if ($config_list->check_config ('LabVIEW_RT')) { + $P = new PerlACE::ProcessLVRT ($program,$arguments); + } + else { + $P = new PerlACE::Process ($program,$arguments); + } + + ### Try to run the program + + if (! -e $P->Executable ()) { + print STDERR "Error: " . $P->Executable () . + " does not exist or is not runnable\n"; + chdir $start_dir; + return; + } + } + + my $start_time = time(); + $status = $P->SpawnWaitKill (400); + my $time = time() - $start_time; + + ### Check for problems + + if ($status == -1) { + print STDERR "Error: $program FAILED (time out)\n"; + $P->Kill (); + $P->TimedWait (1); + } + elsif ($status != 0) { + print STDERR "Error: $program $arguments FAILED with exit status $status\n"; + } + + print "\nauto_run_tests_finished: tests/$program $arguments Time:$time"."s Result:$status\n"; + + check_log ($program); + + if ($config_list->check_config ('Codeguard')) { + check_codeguard_log ($program); + } + chdir $start_dir; +} + +################################################################################ + +sub run_vxworks_command ($) +{ + my $program = shift; + + unlink <log/$program*.log>; + unlink "core"; + + my $P = new PerlACE::ProcessVX ($program); + + ## check module existence + if (! -e $P->Executable ()) { + print STDERR "Error: " . $P->Executable() . + " does not exist\n"; + return; + } + + print "auto_run_tests: tests/$program\n"; + my $start_time = time(); + $status = $P->SpawnWaitKill (400); + my $time = time() - $start_time; + + ### Check for problems + + if ($status == -1) { + print STDERR "Error: $program FAILED (time out)\n"; + $P->Kill (); + $P->TimedWait (1); + } + elsif ($status != 0) { + print STDERR "Error: $program FAILED with exit status $status\n"; + } + + print "\nauto_run_tests_finished: test/$program Time:$time"."s Result:$status\n"; + + check_log ($program); +} + +################################################################################ + +sub output_vxworks_commands +{ + my($oh) = shift; + my($program) = shift; + my($length) = length($program) + 2; + + if (defined $ENV{'ACE_RUN_VX_CHECK_RESOURCES'}) { + print $oh "memShow();\n"; + } + + print $oh "write(2, \"\\n$program\\n\", $length);\n" . + "ld 1,0, \"" . $program . ".out\"\n" . + "vx_execae ace_main\n" . + "unld \"" . $program . ".out\"\n"; +} + +################################################################################ + +sub purify_program ($) +{ + ### @todo + + my $program = shift; + + $program_exe = $program; + + print STDERR "Purifying $program\n"; + + system ("purify ". + "/run ". + "/save-data=purify_results\$program.pfy ". + "/save-text-data=purify_results\$program.txt ". + "/AllocCallStackLength=20 ". + "/ErrorCallStackLength=20 ". + "/HandlesInUseAtExit ". + "/InUseAtExit ". + "/LeaksAtExit ". + "$program_exe"); +} + +################################################################################ + +sub check_log ($) +{ + my $program = shift; + + ### Check the logs + local $log_suffix; + if (defined $ENV{"ACE_WINCE_TEST_CONTROLLER"}) { + $log_suffix = ".txt"; + } + else { + $log_suffix = ".log"; + } + + # Support logs generated by tests in subdirectories, such as tests + # found in the SSL subdirectory. + local $the_program = basename($program); + local $log = "log/".$the_program.$log_suffix; + + if (-e "core") { + print STDERR "Error: $program dumped core\n"; + unlink "core"; + } + + if (! -e $log ) { + print STDERR "Error: No log file ($log) is present\n"; + } + else { + if (open (LOG, "<".$log) == 0) { + print STDERR "Error: Cannot open log file $log\n"; + } + else { + my $print_log = 0; + my $starting_matched = 0; + my $ending_matched = 0; + + while (<LOG>) { + chomp; + + if (m/Starting/) { + $starting_matched = 1; + } + + if (m/Ending/) { + $ending_matched = 1; + } + + if (/LM\_ERROR\@(.*)$/) { + print STDERR "Error: ($log): $1\n"; + $print_log = 1; + } + if (/LM\_WARNING\@(.*)$/) { + print STDERR "Warning: ($log): $1\n"; + $print_log = 1; + } + } + + close (LOG); # ignore errors + + if ($starting_matched == 0) { + print STDERR "Error ($log): no line with 'Starting'\n"; + $print_log = 1; + } + + if ($ending_matched == 0) { + print STDERR "Error ($log): no line with 'Ending'\n"; + $print_log = 1; + } + + if ($print_log == 1) { + print STDERR "======= Begin Log File \n"; + if (open (LOG, "<".$log) == 0) { + print STDERR "Error: Cannot open log file $log\n"; + } + else { + my @log = <LOG>; + print STDERR @log; + close (LOG); + } + print STDERR "======= End Log File \n"; + } + + # Now check for any sub-logs. If either the main log or a + # sub-log has an error, print the sub-log. + opendir (THISDIR, "log"); + local $sublognames = "$program\-.*".$log_suffix; + @sublogs = grep (/^$sublognames/, readdir (THISDIR)); + closedir (THISDIR); + my $saw_short_process_manager_child_log = 0; + foreach $log (@sublogs) { + # Just like the main log, but note that Process_Manager_Test + # kills (signal 9) one of its children so the log may get + # deleted, or it may be incomplete. So let this one go, but + # only once per Process_Manager_Test. + if (open (LOG, "<log/".$log) == 0) { + print STDERR "Error: Cannot open sublog file $log\n"; + } + else { + my $number_starting = 0; + my $number_ending = 0; + while (<LOG>) { + chomp; + if (m/Starting/) { + $number_starting++; + } + if (m/Ending/) { + $number_ending++; + } + if (/LM\_ERROR\@(.*)$/) { + print STDERR "Error: ($log): $1\n"; + $print_log = 1; + } + if (/LM\_WARNING\@(.*)$/) { + print STDERR "Warning: ($log): $1\n"; + $print_log = 1; + } + } + + if ($number_starting == 0) { + print STDERR "Error ($log): no line with 'Starting'\n"; + $print_log = 1; + } + + if ($number_ending == 0) { + if ($program eq 'Process_Manager_Test' && + $saw_short_process_manager_child_log == 0) { + $saw_short_process_manager_child_log = 1; + $number_ending = 1; + } + else { + print STDERR "Error ($log): no line with 'Ending'\n"; + $print_log = 1; + } + } + + if ($number_starting != $number_ending) { + print STDERR "Error ($log): Number of 'Starting' does not match number of 'Ending' ($number_starting != $number_ending)\n"; + $print_log = 1; + } + + close (LOG); # ignore errors + if ($print_log == 1) { + print STDERR "======= Begin Sublog File ".$log."\n"; + if (open (LOG, "<log/".$log) == 0) { + print STDERR "Error: Cannot open sublog file $log\n"; + } + else { + my @log = <LOG>; + print STDERR @log; + close (LOG); + } + print STDERR "======= End Sublog File \n"; + } + } + } + } + } +} + +sub check_codeguard_log ($) +{ + my $program = shift; + + ### Check the logs + + local $log = $program.".cgl"; + + if (-e $log ) { + print STDERR "======= Begin Codeguard Log File \n"; + if (open (LOG, "<".$log) == 0) { + print STDERR "Error: Cannot open codeguard log file $log\n"; + } + else { + my @log = <LOG>; + print STDERR @log; + close (LOG); + } + print STDERR "======= End Codeguard Log File \n"; + } +} + +################################################################################ + +sub delete_temp_files () +{ + my @files = ('ace_pipe_name', 'pattern'); + my $file = ''; + + if (!opendir (DIR, $tmp)) { + warn "Cannot open temp directory $tmp\n"; + return; + } + + foreach $file (readdir (DIR)) { + if ($file =~ /^ace_temp_file/ || $file =~ /^Naming_Test/) { + push @files, $tmp . '/' . $file; + } + } + closedir (DIR); + + PerlACE::check_n_cleanup_files ('MEM_Acceptor_*'); + PerlACE::check_n_cleanup_files ('backing_store_*'); +} + +################################################################################ + +if (!getopts ('dhtvo:') || $opt_h) { + print "run_test.pl [-h] [-v] [-o <output file>] [-t file1 file2 ...]\n"; + print "\n"; + print "Runs the tests listed in run_test.lst\n"; + print "\n"; + print "Options:\n"; + print " -d Debug mode (do not run tests)\n"; + print " -h Display this help\n"; + print " -t Runs all the tests passed via the cmd line\n"; + print " -v Generate commands for VxWorks\n"; + print " -o Put VxWorks commands in <output file>\n"; + print "\n"; + print "Pass in configs using \"-Config XXXXX\"\n"; + print "\n"; + print "Possible Configs: CHECK_RESOURCES Purify Codeguard Valgrind ", + $config_list->list_configs (), "\n"; + exit (1); +} + +## since we can't use "our" to get rid of warnings. +$opt_h = $opt_h if (defined $opt_h); +$opt_t = $opt_t if (defined $opt_t); +$opt_g = $opt_g if (defined $opt_g); + +if (!($tmp = $ENV{TMP}) && !($tmp = $ENV{TEMP})) { + $tmp="/tmp"; +} + +check_for_more_configs (); + +if (PerlACE::is_vxworks_test ()) { + $opt_v = 1; +} + +@tests = (); + +if (defined $opt_t) { + @tests = @ARGV; +} +else { + $config_list->load ("run_test.lst"); + @tests = $config_list->valid_entries (); +} + +if (defined $opt_d) { + $config_list->dump (); +} + +record_resources () if (!defined $opt_d); + +my($oh) = \*STDOUT; +if (defined $opt_v && defined $opt_o) { + $oh = new FileHandle(); + if ($opt_o != 1) { + if (!open($oh, ">$opt_o")) { + print STDERR "ERROR: Unable to write to $opt_o\n"; + exit(1); + } + } + + print $oh "#\n" . + "# ACE one-button test for VxWorks 5.x.\n" . + "# To use: -> < run_test.vxworks > run_test.log\n" . + "#\n" . + "# NOTE: if you build with a shared ACE library, be sure to load\n" . + "# that first:\n" . + "# -> ld < ../ace/libACE.so\n" . + "# and unld it after running the tests.\n" . + "#\n" . + "# The output logs can be checked from a Unix host:\n" . + "# % ./run_tests.check log/*.log\n\n"; + + foreach $test (@tests) { + output_vxworks_commands ($oh, $test); + } +} +else { + + my $target = PerlACE::TestTarget::create_target ($PerlACE::TestConfig); + + foreach $test (@tests) { + if (defined $opt_d) { + print "Would run test $test now\n"; + } + elsif ($config_list->check_config ('Purify')) { + purify_program ($test); + } + if (defined $opt_v) { + run_vxworks_command ($test); + } + else { + run_program ($test); + } + $target->GetStderrLog(); + } +} + +check_resources ($oh) if (!defined $opt_d); + +delete_temp_files (); diff --git a/ACE/tests/run_tests.check b/ACE/tests/run_tests.check new file mode 100755 index 00000000000..2f6ee6ccd34 --- /dev/null +++ b/ACE/tests/run_tests.check @@ -0,0 +1,41 @@ +#! /bin/sh -f +# $Id$ +# +# Checks one ore more ACE test log file(s) for expected results. + +IFS="|" +tmp=/tmp + +# These patterns must be contained in log file. +SUCCESS_MSGS="Starting|Ending" + +# These patterns should not be contained in log file. +if [ "$1" != "log/Cached_Accept_Conn_Test.log" ]; then + ERROR_MSGS="assertion failed|not supported|No such file or directory|Invalid argument|timeout|Bad file number" +else + # "No such file or directory" is allowed in Cached_Accept_Conn_Test.log + ERROR_MSGS="assertion failed|not supported|Invalid argument|timeout|Bad file number" +fi +status=0 + +for arg do + for i in $SUCCESS_MSGS; do + egrep $i $arg >/dev/null 2>&1 + if [ $? -eq 1 ]; then + echo Error in $arg: no line with $i + status=1 + fi + done + + for i in $ERROR_MSGS; do + #### The /dev/null arg to egrep causes the filename to be printed + #### out. The sed command strips off the leading 'log/' and + #### trailing '.log'. + egrep $i $arg /dev/null | sed -e 's%^log/%%' -e 's%[.]log:%: %' + if [ $? -ne 0 ]; then status=1; fi + done +done + +exit $status + +# EOF diff --git a/ACE/tests/run_tests_remote.sh b/ACE/tests/run_tests_remote.sh new file mode 100755 index 00000000000..149a9174d92 --- /dev/null +++ b/ACE/tests/run_tests_remote.sh @@ -0,0 +1,246 @@ +#!/bin/sh +# $Id$ +# +# This is the UNIX version of the one-button ACE tests. +# Contributed originally by Michael Rueger <m_rueger@SYSCOMP.DE> and +# modified substantially by the DOC group. +# +# It also supports remote invocation on a CHORUS/ClassiX/MVME target. +# For that environment, these steps are required: +# 1) Create a "log" directory below the root mount point on the host. +# 2) Mount the "tests" directory on the target, and add it to the PATH +# on the target. +# 3) cd to the directory that contains this script on the host. +# 4) Create a symlink to the "log" directory create in step 1) above. +# 5) ./run_tests.sh <target_hostname> + +if [ -x /bin/rm ]; then + RM=/bin/rm +elif [ -x /usr/bin/rm ]; then + RM=/usr/bin/rm +else + echo "Can't find rm, aborting." 1>&2 + exit 1 +fi + +usage="usage: $0 [-p] <target> + -p: purify tests" + +purify=0 +purify_with_old_gcc=0 + +#### +#### Interpret command arguments. +#### +for arg in "$@"; do + case $arg in + -p ) purify=1 + shift + ;; + + -'?' ) echo $usage + exit 0 + ;; + + -*) echo $0: unknown option $arg + echo $usage + exit 1 + ;; + esac +done + +if [ ! "$ACE_ROOT" ]; then + ACE_ROOT=.. + export ACE_ROOT +fi + +# Some tests fork/exec copies of themselves (e.g. Pipe_Test). If execvp +# ends up getting used, the PATH has to include "." or the test won't work. +PATH=.:$PATH +export PATH + +IFS="|" +tmp=/tmp +compilation_log="log/compilations.log" +shlib_suffix=".so" + +LD_LIBRARY_PATH=$ACE_ROOT/ace:${LD_LIBRARY_PATH:-/usr/lib} +export LD_LIBRARY_PATH + +#### If uname isn't on the user's PATH, store any string +#### in $sysname. +sysname=`uname -s 2>&1` + +if [ $sysname = 'HP-UX' ]; then + SHLIB_PATH=$ACE_ROOT/ace:${SHLIB_PATH:-/usr/lib} + export SHLIB_PATH + shlib_suffix=".sl" +fi + +if [ $sysname = 'AIX' ]; then + LIBPATH=$ACE_ROOT/ace:${LIBPATH:-/usr/lib:/lib} + export LIBPATH +fi + +if echo $sysname | grep -q CYGWIN; then + shlib_suffix=".dll" +fi + +if [ $purify -eq 1 ]; then + if echo $PWD | egrep 'gcc|g++'; then + purify_with_old_gcc=1 + fi +fi + +#### +#### Process command line arguments. +#### +target= +run_command= +chorus=1 +if [ $# -eq 1 ]; then + target=$1 + if rsh $target help | head -1 | egrep -i CHORUS > /dev/null; then + run_command=arun + chorus= + else + #### Only support Chorus/MVME, for now. + echo $0: host $1 does not appear to be Chorus/MVME: not supported. + exit 1 + fi +elif [ $# -ne 0 ]; then + echo $usage + exit 1 +fi + +run() +{ + $RM -f core log/$1.log + + if [ ! -f "$1" ]; then + echo 1>&2 "Making $1 . . ." + remove_exe_after_test="true" + make BIN="$1" >> "$compilation_log" + else + remove_exe_after_test="" + fi + + echo "running $1" + if [ -z "$chorus" ]; then + #### Assumes that the PATH has been set on the target. + rsh $target $run_command $1 + else + ./$1 + fi + status=$? + + if [ $status -ne 0 ]; then + echo \"$1\" FAILED with exit status $status!!!! + fi + + if [ -f core ]; then + echo \"$1\" dumped core!!!! + fi + + if [ -f log/$1.log ]; then + sh ./run_tests.check log/$1.log + else + echo "No log file (log/$1.log) is present" + fi + + if [ "$remove_exe_after_test" ]; then + echo 1>&2 "Discarding $1" + rm -f "$1" ".obj/$1.o" + fi +} + +LynxOS=1 +if [ $sysname = 'LynxOS' ]; then + LynxOS= +fi + +Unicos=1 +if [ $sysname = 'unicos' ]; then + Unicos= +fi + +ace_version=`head -1 ../VERSION | sed 's/.*version \([0-9.]*\).*/\1/'` + +if [ ! "$chorus" ]; then + user=${LOGNAME:-`whoami`} + start_test_resources=`ipcs | egrep $user` +fi # ! chorus + +ACE_BUILD_COMPONENTS=`$ACE_ROOT/bin/ace_components --ace` +OTHER=`echo $ACE_BUILD_COMPONENTS | egrep ' Other '` +TOKEN=`echo $ACE_BUILD_COMPONENTS | egrep ' Token '` + +libDLL_Test= +if [ -f libDLL_Test$shlib_suffix ]; then + libDLL_Test=1 +fi + +netsvcs_main= +if [ -f ../netsvcs/servers/main ]; then + netsvcs_main=1 +fi + +# Testing for this flag always disables the test +DISABLED= + +echo "Starting ACE version $ace_version tests . . ." + +mv -f "$compilation_log" "$compilation_log.bak" > /dev/null 2>&1 + +if [ $sysname != 'procnto' ]; then + # Limit the amount of memory required by the tests to 64Mb. + ulimit -d 65536 +fi # ! procnto + +# Redirection gets confused when rsh is involved (Chorus) +for i in `cat run_tests_remote.lst | tr "\012" "$IFS"`; do + + if [ "$i" != "" ]; then + + case $i in + */*) + p=`dirname $i | sed 's%/% \&\& test $%g'`; + precond="\$$p"; + test=`basename $i`; + ;; + *) + precond=""; + test=$i; + ;; + esac + +# echo =****= $precond ===== $test; + + if test -z "$precond"; then + run $test + elif eval test $precond; then + run $test + else + if echo $precond | egrep '(DISABLED)|(OTHER)' > /dev/null; then :; else + echo Skipping $test on this platform + fi + fi + fi +done + +echo "Finished ACE version $ace_version tests." + +$RM -f ace_pipe_name pattern \ + $tmp/ace_temp_file* \ + $tmp/ace_test_file \ + $tmp/Naming_Test* + +if [ ! "$chorus" ]; then + end_test_resources=`ipcs | egrep $user` + if [ "$start_test_resources" != "$end_test_resources" ]; then + echo WARNING: the ACE tests _may_ have leaked OS resources! + ipcs + fi +fi + +# EOF diff --git a/ACE/tests/test_config.h b/ACE/tests/test_config.h new file mode 100644 index 00000000000..144eaf63928 --- /dev/null +++ b/ACE/tests/test_config.h @@ -0,0 +1,176 @@ +// -*- C++ -*- + +// ============================================================================ +/** + * @file test_config.h + * + * $Id$ + * + * This file factors out common macros and other utilities used by the + * ACE automated regression tests. It also shows how to redirect ACE_DEBUG/ACE_ERROR + * output to a file. + * + * @author Prashant Jain <pjain@cs.wustl.edu> + * @author Tim Harrison <harrison@cs.wustl.edu> + * @author David Levine <levine@cs.wustl.edu> + */ +// ============================================================================ + +#ifndef ACE_TEST_CONFIG_H +#define ACE_TEST_CONFIG_H + +// This first #undef protects against command-line definitions. +#undef ACE_NDEBUG + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_NLOGGING) +// ACE_NLOGGING must not be set if the tests are to produce any output. +#undef ACE_NLOGGING +#endif /* ACE_NLOGGING */ + +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_stdio.h" +#include "ace/Log_Msg.h" + +#if defined (ACE_HAS_WINCE) +// Note that Pocket PC 2002 will NOT create a directory if it does not start with a leading '\'. +// PPC 2002 only accepts '\log' as a valid directory name, while 'log\' works under WinCE 3.0. +# define ACE_LOG_DIRECTORY_FOR_MKDIR ACE_TEXT ("\\log") +# define ACE_LOG_DIRECTORY ACE_TEXT ("\\log\\") +# define MAKE_PIPE_NAME(X) ACE_TEXT ("\\\\.\\pipe\\"#X) +#elif defined (ACE_WIN32) +# define ACE_LOG_DIRECTORY ACE_TEXT ("log\\") +# define MAKE_PIPE_NAME(X) ACE_TEXT ("\\\\.\\pipe\\"#X) +#else +# define ACE_LOG_DIRECTORY ACE_TEXT ("log/") +# define MAKE_PIPE_NAME(X) ACE_TEXT (X) +#endif /* ACE_WIN32 */ + +#if defined (ACE_HAS_WINCE) +#define ACE_LOG_FILE_EXT_NAME ACE_TEXT (".txt") +#else +#define ACE_LOG_FILE_EXT_NAME ACE_TEXT (".log") +#endif /* ACE_HAS_WINCE */ + +#if defined (ACE_HAS_WINCE) || defined (ACE_HAS_PHARLAP) +const size_t ACE_MAX_CLIENTS = 4; +#else +const size_t ACE_MAX_CLIENTS = 30; +#endif /* ACE_HAS_WINCE */ + +const size_t ACE_NS_MAX_ENTRIES = 1000; +const size_t ACE_DEFAULT_USECS = 1000; +const size_t ACE_MAX_TIMERS = 4; +const size_t ACE_MAX_DELAY = 10; +const size_t ACE_MAX_INTERVAL = 0; +const size_t ACE_MAX_ITERATIONS = 10; +const size_t ACE_MAX_PROCESSES = 10; +const size_t ACE_MAX_THREADS = 4; + +#ifndef ACE_START_TEST +#define ACE_START_TEST(NAME) \ + const ACE_TCHAR *program = NAME; \ + ACE_LOG_MSG->open (program, ACE_Log_Msg::OSTREAM | ACE_Log_Msg::VERBOSE_LITE); \ + if (ace_file_stream::instance()->set_output (program) != 0) \ + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("set_output failed")), -1); \ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Starting %s test at %D\n"), program)) +#endif /* ACE_START_TEST */ + +#ifndef ACE_END_TEST +#define ACE_END_TEST \ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Ending %s test at %D\n"), program)); \ + ace_file_stream::instance()->close () +#endif /* ACE_END_TEST */ + +#define ACE_CLOSE_TEST_LOG ace_file_stream::instance()->close () + +#define ACE_APPEND_LOG(NAME) \ + const ACE_TCHAR *program = NAME; \ + ACE_LOG_MSG->open (program, ACE_Log_Msg::OSTREAM | ACE_Log_Msg::VERBOSE_LITE); \ + if (ace_file_stream::instance()->set_output (program, 1) != 0) \ + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("set_output failed")), -1); \ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Starting %s test at %D\n"), program)); + +#define ACE_END_LOG \ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Ending %s test at %D\n\n"), program)); \ + ACE_LOG_MSG->set_flags(ACE_Log_Msg::SILENT); \ + ace_file_stream::instance()->close (); + +#if defined (ACE_VXWORKS) + // This is the only way I could figure out to avoid an error + // about attempting to unlink a non-existent file. + +#include "ace/OS_NS_fcntl.h" + +#define ACE_INIT_LOG(NAME) \ + ACE_TCHAR temp[MAXPATHLEN]; \ + ACE_OS::sprintf (temp, ACE_TEXT ("%s%s%s"), \ + ACE_LOG_DIRECTORY, \ + ACE::basename (NAME, ACE_DIRECTORY_SEPARATOR_CHAR), \ + ACE_LOG_FILE_EXT_NAME); \ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Deleting old log file %s (if any)\n\n"), temp)); \ + int fd_init_log; \ + if ((fd_init_log = ACE_OS::open (temp, \ + O_WRONLY|O_CREAT, \ + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) != ERROR) \ + { \ + ACE_OS::close (fd_init_log); \ + ACE_OS::unlink (temp); \ + } + +#if defined (ghs) +# // Rename main to ace_main for compatibility with run_tests.vxworks. +# undef ACE_MAIN +# define ACE_MAIN ace_main +#endif /* ghs */ +#else /* ! VXWORKS */ +# if !defined (ACE_WIN32) && defined (ACE_USES_WCHAR) +# define ACE_INIT_LOG_FMT ACE_TEXT ("%ls%ls%ls") +# else +# define ACE_INIT_LOG_FMT ACE_TEXT ("%s%s%s") +# endif /* !ACE_WIN32 && ACE_USES_WCHAR */ +#define ACE_INIT_LOG(NAME) \ + ACE_TCHAR temp[MAXPATHLEN]; \ + ACE_OS::sprintf (temp, ACE_INIT_LOG_FMT, \ + ACE_LOG_DIRECTORY, \ + ACE::basename (NAME, ACE_DIRECTORY_SEPARATOR_CHAR), \ + ACE_LOG_FILE_EXT_NAME); \ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Deleting old log file %s (if any)\n\n"), temp)); \ + ACE_OS::unlink (temp); +#endif /* ! VXWORKS */ + +#if defined (ACE_LACKS_IOSTREAM_TOTALLY) +#define OFSTREAM FILE +#else +#define OFSTREAM ofstream +#endif /* ACE_LACKS_IOSTREAM_TOTALLY */ + +#include "tests/Test_Output_Export.h" + +class Test_Output_Export ACE_Test_Output +{ +public: + ACE_Test_Output (void); + ~ACE_Test_Output (void); + static ACE_Test_Output *instance (void); + int set_output (const ACE_TCHAR *filename, int append = 0); + OFSTREAM *output_file (void); + void close (void); + const ACE_TCHAR *dll_name (void); + const ACE_TCHAR *name (void); + static void close_singleton (void); + +private: + static ACE_Test_Output *instance_; + + OFSTREAM *output_file_; +}; + +typedef ACE_Test_Output ace_file_stream; + +#endif /* ACE_TEST_CONFIG_H */ diff --git a/ACE/tests/tests.mpc b/ACE/tests/tests.mpc new file mode 100644 index 00000000000..e9e60e7f6cc --- /dev/null +++ b/ACE/tests/tests.mpc @@ -0,0 +1,1481 @@ +// -*- MPC -*- +// $Id$ + +project(Test_Output) : acelib, script { + sharedname = Test_Output + dynamicflags = TEST_OUTPUT_BUILD_DLL + Source_Files { + Test_Output.cpp + } + Header_Files { + Test_Output_Export.h + test_config.h + } + Resource_Files { + } + Script_Files { + run_test.pl + run_test.lst + } + + verbatim(automake,local) { +dist_check_SCRIPTS = run_test.pl run_test.lst +TESTS = $(noinst_PROGRAMS) +TESTS_ENVIRONMENT = $(srcdir)/run_test.pl -t + } +} + +project(Framework Component DLL) : acelib { + sharedname = Framework_Component_DLL + dynamicflags = FRAMEWORK_COMPONENT_DLL_BUILD_DLL + + Source_Files { + Framework_Component_DLL.cpp + } + Header_Files { + Framework_Component_DLL.h + Framework_Component_DLL_Export.h + } + Resource_Files { + } +} + + +project(DLL Test Parent Lib) : acelib { + sharedname = DLL_Test_Parent + dynamicflags = DLL_TEST_PARENT_BUILD_DLL + + Source_Files { + DLL_Test_Parent.cpp + } + Header_Files { + DLL_Test_Parent_Export.h + test_config.h + } + Resource_Files { + } +} + +project(DLL Test Lib) : acelib, dll_test_parent_lib { + sharedname = DLL_Test_Lib + dynamicflags = ACE_SVC_BUILD_DLL + + Source_Files { + DLL_Test_Impl.cpp + } + Header_Files { + test_config.h + } + Resource_Files { + } +} + +project(Based Pointer Test Lib) : acelib { + avoids += ace_for_tao + sharedname = Based_Pointer_Test_Lib + dynamicflags = ACE_SVC_BUILD_DLL + + Source_Files { + Based_Pointer_Test_Lib.cpp + } + Resource_Files { + } +} + +project(Service Config DLL) : acelib { + sharedname = Service_Config_DLL + dynamicflags = SERVICE_CONFIG_DLL_BUILD_DLL + + Source_Files { + Service_Config_DLL.cpp + } + Header_Files { + Service_Config_DLL.h + Service_Config_DLL_Export.h + } + Resource_Files { + } +} + +project(ACE Init Test) : acetest { + exename = ACE_Init_Test + Source_Files { + ACE_Init_Test.cpp + } + Resource_Files { + ACE_Init_Test.rc + } +} + +project(ACE Test) : acetest { + exename = ACE_Test + Source_Files { + ACE_Test.cpp + } +} + +project(Aio Platform Test) : acetest { + exename = Aio_Platform_Test + Source_Files { + Aio_Platform_Test.cpp + } +} + +project(Arg Shifter Test) : acetest { + exename = Arg_Shifter_Test + Source_Files { + Arg_Shifter_Test.cpp + } +} + +project(Array Map Test) : acetest { + exename = Array_Map_Test + Source_Files { + Array_Map_Test.cpp + } +} + +project(ARGV Test) : acetest { + exename = ARGV_Test + Source_Files { + ARGV_Test.cpp + } +} + +project(Atomic Op Test) : acetest { + exename = Atomic_Op_Test + Source_Files { + Atomic_Op_Test.cpp + } +} + +project(Auto Event Test) : acetest { + exename = Auto_Event_Test + Source_Files { + Auto_Event_Test.cpp + } +} + +project(Auto IncDec Test) : acetest { + exename = Auto_IncDec_Test + Source_Files { + Auto_IncDec_Test.cpp + } +} + +project(Barrier Test) : acetest { + exename = Barrier_Test + Source_Files { + Barrier_Test.cpp + } +} + +project(Basic Types Test) : acetest { + exename = Basic_Types_Test + Source_Files { + Basic_Types_Test.cpp + } +} + +project(Bound Ptr Test) : acetest { + avoids += ace_for_tao + exename = Bound_Ptr_Test + Source_Files { + Bound_Ptr_Test.cpp + } +} + +project(Buffer Stream Test) : acetest { + exename = Buffer_Stream_Test + Source_Files { + Buffer_Stream_Test.cpp + } +} + +project(Bug_1576_Regression_Test) : acetest { + exename = Bug_1576_Regression_Test + Source_Files { + Bug_1576_Regression_Test.cpp + } +} + +project(Bug_1890_Regression_Test) : acetest { + exename = Bug_1890_Regression_Test + Source_Files { + Bug_1890_Regression_Test.cpp + } +} + +project(Bug_2368_Regression_Test) : acetest { + exename = Bug_2368_Regression_Test + Source_Files { + Bug_2368_Regression_Test.cpp + } +} + +project(Bug_2497_Regression_Test) : acetest { + exename = Bug_2497_Regression_Test + Source_Files { + Bug_2497_Regression_Test.cpp + } +} + +project(Bug_2540_Regression_Test) : acetest { + exename = Bug_2540_Regression_Test + Source_Files { + Bug_2540_Regression_Test.cpp + } +} + +project(Bug_2653_Regression_Test) : acetest { + exename = Bug_2653_Regression_Test + Source_Files { + Bug_2653_Regression_Test.cpp + } +} + +project(Bug_2815_Regression_Test) : acetest { + exename = Bug_2815_Regression_Test + Source_Files { + Bug_2815_Regression_Test.cpp + } +} + +project(Bug_2820_Regression_Test) : acetest { + exename = Bug_2820_Regression_Test + Source_Files { + Bug_2820_Regression_Test.cpp + } +} + +project(Bug_2975_Regression_Test) : acetest { + exename = Bug_2975_Regression_Test + Source_Files { + Bug_2975_Regression_Test.cpp + } +} + +project(Bug_3102_Regression_Test) : acetest { + exename = Bug_3102_Regression_Test + Source_Files { + Bug_3102_Regression_Test.cpp + } +} + +project(Cache Map Manager Test) : acetest { + exename = Cache_Map_Manager_Test + Source_Files { + Cache_Map_Manager_Test.cpp + } +} + +project(Memcpy_Test) : acetest { + avoids += ace_for_tao + exename = Memcpy_Test + Source_Files { + Memcpy_Test.cpp + } +} + +project(Cached Accept Conn Test) : acetest { + avoids += ace_for_tao + exename = Cached_Accept_Conn_Test + Source_Files { + Cached_Accept_Conn_Test.cpp + } +} + +project(Cached Allocator Test) : acetest { + avoids += ace_for_tao + exename = Cached_Allocator_Test + Source_Files { + Cached_Allocator_Test.cpp + } +} + +project(Cached Conn Test) : acetest { + avoids += ace_for_tao + exename = Cached_Conn_Test + Source_Files { + Cached_Conn_Test.cpp + } +} + +project(Capabilities Test) : acetest { + avoids += ace_for_tao + exename = Capabilities_Test + Source_Files { + Capabilities_Test.cpp + } +} + +project(CDR File Test) : acetest { + avoids += ace_for_tao + exename = CDR_File_Test + Source_Files { + CDR_File_Test.cpp + CE_fostream.cpp + } +} + +project(CDR Test) : acetest { + exename = CDR_Test + Source_Files { + CDR_Test.cpp + } +} + +project(Collection Test) : acetest { + exename = Collection_Test + Source_Files { + Collection_Test.cpp + } +} + +project(Config Test) : acetest { + avoids += ace_for_tao + exename = Config_Test + Source_Files { + Config_Test.cpp + } + Documentation_Files { + Config_Test.ini + Config_Test_Import_1.ini + } +} + +project(Conn Test) : acetest { + avoids += ace_for_tao + exename = Conn_Test + Source_Files { + Conn_Test.cpp + } +} + +project(Date Time Test) : acetest { + avoids += ace_for_tao + exename = Date_Time_Test + Source_Files { + Date_Time_Test.cpp + } +} + +project(Dev Poll Reactor Test) : acetest { + exename = Dev_Poll_Reactor_Test + Source_Files { + Dev_Poll_Reactor_Test.cpp + } +} + +project(Dirent Test) : acetest { + + exename = Dirent_Test + Source_Files { + Dirent_Test.cpp + } +} + +project(DLList Test) : acetest { + avoids += ace_for_tao + exename = DLList_Test + Source_Files { + DLList_Test.cpp + } +} + +project(DLL Test) : acetest, dll_test_parent_lib { + after += DLL_Test_Lib + libs += DLL_Test_Lib + exename = DLL_Test + Source_Files { + DLL_Test.cpp + } +} + +project(Dynamic Test) : acetest { + exename = Dynamic_Test + Source_Files { + Dynamic_Test.cpp + } +} + +project(Enum Interfaces Test) : acetest { + exename = Enum_Interfaces_Test + Source_Files { + Enum_Interfaces_Test.cpp + } +} + +project(Env Value Test) : acetest { + exename = Env_Value_Test + Source_Files { + Env_Value_Test.cpp + } +} + +project(Future Test) : acetest { + avoids += ace_for_tao + exename = Future_Test + Source_Files { + Future_Test.cpp + } +} + +project(Future Set Test) : acetest { + avoids += ace_for_tao + exename = Future_Set_Test + Source_Files { + Future_Set_Test.cpp + } +} + +project(Get Opt Test) : acetest { + exename = Get_Opt_Test + Source_Files { + Get_Opt_Test.cpp + } +} + +project(Handle Set Test) : acetest { + avoids += ace_for_tao + exename = Handle_Set_Test + Source_Files { + Handle_Set_Test.cpp + } +} + +project(High Res Timer Test) : acetest { + avoids += ace_for_tao + exename = High_Res_Timer_Test + Source_Files { + High_Res_Timer_Test.cpp + } +} + +project(Hash Map Manager Test) : acetest { + exename = Hash_Map_Manager_Test + Source_Files { + Hash_Map_Manager_Test.cpp + } + Template_Files { + STL_algorithm_Test_T.cpp + } +} + +project(Hash Multi Map Manager Test) : acetest { + exename = Hash_Multi_Map_Manager_Test + Source_Files { + Hash_Multi_Map_Manager_Test.cpp + } +} + +project(Hash Map Bucket Iterator Test) : acetest { + exename = Hash_Map_Bucket_Iterator_Test + Source_Files { + Hash_Map_Bucket_Iterator_Test.cpp + } +} + +project(INET Addr Test) : acetest { + exename = INET_Addr_Test + Source_Files { + INET_Addr_Test.cpp + } +} + +project(Integer_Truncate Test) : acetest { + exename = Integer_Truncate_Test + Source_Files { + Integer_Truncate_Test.cpp + } +} + +project(IOStream Test) : acetest { + exename = IOStream_Test + Source_Files { + IOStream_Test.cpp + } +} + +project(Lazy Map Manager Test) : acetest { + exename = Lazy_Map_Manager_Test + Source_Files { + Lazy_Map_Manager_Test.cpp + } +} + +project(Log Msg Test) : acetest { + avoids += ace_for_tao + exename = Log_Msg_Test + Source_Files { + Log_Msg_Test.cpp + } +} + +project(Log Msg Backend Test) : acetest { + avoids += ace_for_tao + exename = Log_Msg_Backend_Test + Source_Files { + Log_Msg_Backend_Test.cpp + } +} + +project(Logging Strategy Test) : acetest { + exename = Logging_Strategy_Test + Source_Files { + Logging_Strategy_Test.cpp + } +} + +project(Malloc Test) : acetest { + avoids += ace_for_tao + exename = Malloc_Test + Source_Files { + Malloc_Test.cpp + } +} + +project(Manual_Event Test) : acetest { + exename = Manual_Event_Test + Source_Files { + Manual_Event_Test.cpp + } +} + +project(Map Test) : acetest { + avoids += ace_for_tao + exename = Map_Test + Source_Files { + Map_Test.cpp + } +} + +project(Map Manager Test) : acetest { + avoids += ace_for_tao + exename = Map_Manager_Test + Source_Files { + Map_Manager_Test.cpp + } +} + +project(Max Default Port Test) : acetest { + exename = Max_Default_Port_Test + Source_Files { + Max_Default_Port_Test.cpp + } +} + +project(MEM Stream Test) : acetest { + avoids += ace_for_tao + exename = MEM_Stream_Test + Source_Files { + MEM_Stream_Test.cpp + } +} + +project(Mem Map Test) : acetest { + avoids += ace_for_tao + exename = Mem_Map_Test + Source_Files { + Mem_Map_Test.cpp + } +} + +project(MM Shared Memory Test) : acetest { + avoids += ace_for_tao + exename = MM_Shared_Memory_Test + Source_Files { + MM_Shared_Memory_Test.cpp + } +} + +project(MT Reactor Timer Test) : acetest { + exename = MT_Reactor_Timer_Test + Source_Files { + MT_Reactor_Timer_Test.cpp + } +} + +project(MT Reactor Upcall Test) : acetest { + exename = MT_Reactor_Upcall_Test + Source_Files { + MT_Reactor_Upcall_Test.cpp + } +} + +project(MT SOCK Test) : acetest { + exename = MT_SOCK_Test + Source_Files { + MT_SOCK_Test.cpp + } +} + +project(Message Block Test) : acetest { + avoids += ace_for_tao + exename = Message_Block_Test + Source_Files { + Message_Block_Test.cpp + } +} + +project(Message Queue Test) : acetest { + avoids += ace_for_tao + exename = Message_Queue_Test + Source_Files { + Message_Queue_Test.cpp + } +} + +project(Message Queue Test Ex) : acetest { + avoids += ace_for_tao + exename = Message_Queue_Test_Ex + Source_Files { + Message_Queue_Test_Ex.cpp + } +} + +project(Message Queue Notifications Test) : acetest { + exename = Message_Queue_Notifications_Test + Source_Files { + Message_Queue_Notifications_Test.cpp + } +} + +project(Multicast Test) : acetest { + exename = Multicast_Test + Source_Files { + Multicast_Test.cpp + } +} + +project(Multihomed INET Addr Test) : acetest { + avoids += ace_for_tao + exename = Multihomed_INET_Addr_Test + Source_Files { + Multihomed_INET_Addr_Test.cpp + } +} + +project(Network_Adapters_Test) : acetest { + exename = Network_Adapters_Test + Source_Files { + Network_Adapters_Test.cpp + } +} + +project(New Fail Test) : acetest { + exename = New_Fail_Test + Source_Files { + New_Fail_Test.cpp + } +} + +project(Notification Queue Unit Test) : acetest { + exename = Notification_Queue_Unit_Test + Source_Files { + Notification_Queue_Unit_Test.cpp + } +} + +project(Notify Performance Test) : acetest { + avoids += ace_for_tao + exename = Notify_Performance_Test + Source_Files { + Notify_Performance_Test.cpp + } +} + +project(Object Manager Test) : acetest { + exename = Object_Manager_Test + Source_Files { + Object_Manager_Test.cpp + } +} + +project(Object Manager Flipping Test) : acetest { + exename = Object_Manager_Flipping_Test + Source_Files { + Object_Manager_Flipping_Test.cpp + } +} + +project(Obstack Test) : acetest { + exename = Obstack_Test + Source_Files { + Obstack_Test.cpp + } +} + +project(OrdMultiSet Test) : acetest { + exename = OrdMultiSet_Test + Source_Files { + OrdMultiSet_Test.cpp + } +} + +project(OS Test) : acetest { + exename = OS_Test + Source_Files { + OS_Test.cpp + } +} + +project(Proactor Scatter Gather Test) : acetest { + avoids += ace_for_tao + exename = Proactor_Scatter_Gather_Test + Source_Files { + Proactor_Scatter_Gather_Test.cpp + } +} + +project(Proactor Test) : acetest { + avoids += ace_for_tao + exename = Proactor_Test + Source_Files { + Proactor_Test.cpp + } +} + +project(Proactor Timer Test) : acetest { + avoids += ace_for_tao + exename = Proactor_Timer_Test + Source_Files { + Proactor_Timer_Test.cpp + } +} + +project(Process Manual Event Test) : acetest { + exename = Process_Manual_Event_Test + Source_Files { + Process_Manual_Event_Test.cpp + } +} + +project(Process Mutex Test) : acetest { + avoids += ace_for_tao + exename = Process_Mutex_Test + Source_Files { + Process_Mutex_Test.cpp + } +} + +project(Process Semaphore Test) : acetest { + avoids += ace_for_tao + exename = Process_Semaphore_Test + Source_Files { + Process_Semaphore_Test.cpp + } +} + +project(Process Strategy Test) : acetest { + avoids += ace_for_tao // Requires ace/File_Lock + exename = Process_Strategy_Test + Source_Files { + Process_Strategy_Test.cpp + } +} + +project(Priority Buffer Test) : acetest { + exename = Priority_Buffer_Test + Source_Files { + Priority_Buffer_Test.cpp + } +} + +project(Dynamic Priority Test) : acetest { + exename = Dynamic_Priority_Test + Source_Files { + Dynamic_Priority_Test.cpp + } +} + +project(Priority Task Test) : acetest { + exename = Priority_Task_Test + Source_Files { + Priority_Task_Test.cpp + } +} + +project(Priority Reactor Test) : acetest { + avoids += ace_for_tao + exename = Priority_Reactor_Test + Source_Files { + Priority_Reactor_Test.cpp + } +} + +project(Process Manager Test) : acetest { + avoids += ace_for_tao + exename = Process_Manager_Test + Source_Files { + Process_Manager_Test.cpp + } +} + +project(Pipe Test) : acetest { + exename = Pipe_Test + Source_Files { + Pipe_Test.cpp + } +} + +project(RB Tree Test) : acetest { + exename = RB_Tree_Test + Source_Files { + RB_Tree_Test.cpp + } +} + +project(Reactors Test) : acetest { + exename = Reactors_Test + Source_Files { + Reactors_Test.cpp + } +} + +project(Reactor Exceptions Test) : acetest { + exename = Reactor_Exceptions_Test + Source_Files { + Reactor_Exceptions_Test.cpp + } +} + +project(Reactor Notify Test) : acetest { + avoids += ace_for_tao + exename = Reactor_Notify_Test + Source_Files { + Reactor_Notify_Test.cpp + } +} + +project(Reactor Notification Queue Test) : acetest { + exename = Reactor_Notification_Queue_Test + Source_Files { + Reactor_Notification_Queue_Test.cpp + } +} + +project(Reactor Dispatch Order Test) : acetest { + exename = Reactor_Dispatch_Order_Test + Source_Files { + Reactor_Dispatch_Order_Test.cpp + } +} + +project(Reactor Performance Test) : acetest { + avoids += ace_for_tao + exename = Reactor_Performance_Test + Source_Files { + Reactor_Performance_Test.cpp + } +} + +project(Reactor Remove Resume Test) : acetest { + exename = Reactor_Remove_Resume_Test + Source_Files { + Reactor_Remove_Resume_Test.cpp + } +} + +project(Reactor Timer Test) : acetest { + avoids += ace_for_tao + exename = Reactor_Timer_Test + Source_Files { + Reactor_Timer_Test.cpp + } +} + +project(Reader Writer Test) : acetest { + exename = Reader_Writer_Test + Source_Files { + Reader_Writer_Test.cpp + } +} + +project(Recursive Condition Bug Test) : acetest { + exename = Recursive_Condition_Bug_Test + Source_Files { + Recursive_Condition_Bug_Test.cpp + } +} + +project(Recursive Condition Test) : acetest { + exename = Recursive_Condition_Test + Source_Files { + Recursive_Condition_Test.cpp + } +} + +project(Recursive Mutex Test) : acetest { + exename = Recursive_Mutex_Test + Source_Files { + Recursive_Mutex_Test.cpp + } +} + +project(Refcounted Auto Ptr Test) : acetest { + avoids += ace_for_tao + exename = Refcounted_Auto_Ptr_Test + Source_Files { + Refcounted_Auto_Ptr_Test.cpp + } +} + +project(Reverse Lock Test) : acetest { + exename = Reverse_Lock_Test + Source_Files { + Reverse_Lock_Test.cpp + } +} + +project(Semaphore Test) : acetest { + avoids += ace_for_tao + exename = Semaphore_Test + Source_Files { + Semaphore_Test.cpp + } +} + +project(Sendfile Test) : acetest { + exename = Sendfile_Test + Source_Files { + Sendfile_Test.cpp + } +} + +project(Signal Test) : acetest { + exename = Signal_Test + Source_Files { + Signal_Test.cpp + } +} + +project(Sigset Ops Test) : acetest { + exename = Sigset_Ops_Test + Source_Files { + Sigset_Ops_Test.cpp + } +} + +project(Simple Message Block Test) : acetest { + exename = Simple_Message_Block_Test + Source_Files { + Simple_Message_Block_Test.cpp + } +} + +project(SOCK Test) : acetest { + exename = SOCK_Test + Source_Files { + SOCK_Test.cpp + } +} + +project(SOCK Dgram Test) : acetest { + exename = SOCK_Dgram_Test + Source_Files { + SOCK_Dgram_Test.cpp + } +} + +project(SOCK Connector Test) : acetest { + exename = SOCK_Connector_Test + Source_Files { + SOCK_Connector_Test.cpp + } +} + +project(SOCK Netlink Test) : acetest { + avoids += ace_for_tao + exename = SOCK_Netlink_Test + Source_Files { + SOCK_Netlink_Test.cpp + } +} + +project(SOCK Send Recv Test) : acetest { + exename = SOCK_Send_Recv_Test + Source_Files { + SOCK_Send_Recv_Test.cpp + } +} + +project(SPIPE Test) : acetest { + avoids += ace_for_tao + exename = SPIPE_Test + Source_Files { + SPIPE_Test.cpp + } +} + +project(SString Test) : acetest { + exename = SString_Test + Source_Files { + SString_Test.cpp + } +} + +project(SV Shared Memory Test) : acetest { + avoids += ace_for_tao + exename = SV_Shared_Memory_Test + Source_Files { + SV_Shared_Memory_Test.cpp + } +} + +project(Svc Handler Test) : acetest { + avoids += ace_for_tao + exename = Svc_Handler_Test + Source_Files { + Svc_Handler_Test.cpp + } +} + +project(Task Test) : acetest { + exename = Task_Test + Source_Files { + Task_Test.cpp + } +} + +project(Task_Ex Test) : acetest { + exename = Task_Ex_Test + Source_Files { + Task_Ex_Test.cpp + } +} + +project(Thread Manager Test) : acetest { + exename = Thread_Manager_Test + Source_Files { + Thread_Manager_Test.cpp + } +} + +project(Thread Mutex Test) : acetest { + exename = Thread_Mutex_Test + Source_Files { + Thread_Mutex_Test.cpp + } +} + +project(Thread Pool Test) : acetest { + exename = Thread_Pool_Test + Source_Files { + Thread_Pool_Test.cpp + } +} + +project(Thread Creation Threshold Test) : acetest { + exename = Thread_Creation_Threshold_Test + Source_Files { + Thread_Creation_Threshold_Test.cpp + } +} + +project(Timeprobe Test) : acetest { + exename = Timeprobe_Test + Source_Files { + Timeprobe_Test.cpp + } +} + +project(Time Service Test) : acetest { + exename = Time_Service_Test + Source_Files { + Time_Service_Test.cpp + } +} + +project(Time Value Test) : acetest { + exename = Time_Value_Test + Source_Files { + Time_Value_Test.cpp + } +} + +project(Timer Queue Test) : acetest { + avoids += ace_for_tao + exename = Timer_Queue_Test + Header_Files { + randomize.h + } + Source_Files { + Timer_Queue_Test.cpp + } +} + +project(Token Strategy Test) : acetest { + exename = Token_Strategy_Test + Source_Files { + Token_Strategy_Test.cpp + } +} + +project(TP Reactor Test) : acetest { + avoids += ace_for_tao + exename = TP_Reactor_Test + Source_Files { + TP_Reactor_Test.cpp + } +} + +project(TSS Test) : acetest { + exename = TSS_Test + Source_Files { + TSS_Test.cpp + } +} + +project(TSS Static Test) : acetest { + exename = TSS_Static_Test + Source_Files { + TSS_Static_Test.cpp + } +} + +project(Vector Test) : acetest { + exename = Vector_Test + Source_Files { + Vector_Test.cpp + } +} + +project(UPIPE SAP Test) : acetest { + avoids += ace_for_tao + exename = UPIPE_SAP_Test + Source_Files { + UPIPE_SAP_Test.cpp + } +} + +project(Upgradable RW Test) : acetest { + avoids += ace_for_tao + exename = Upgradable_RW_Test + Source_Files { + Upgradable_RW_Test.cpp + } +} + +project(Naming Test) : acetest { + avoids += ace_for_tao + exename = Naming_Test + requires += ace_other + Header_Files { + randomize.h + } + Source_Files { + Naming_Test.cpp + } +} + +project(Thread Pool Reactor Test) : acetest { + exename = Thread_Pool_Reactor_Test + requires += ace_other + + Source_Files { + Thread_Pool_Reactor_Test.cpp + } +} + +project(Thread Pool Reactor Resume Test) : acetest { + exename = Thread_Pool_Reactor_Resume_Test + requires += ace_other + + Source_Files { + Thread_Pool_Reactor_Resume_Test.cpp + } +} + +project(XtMotifReactor Test) : acetest, ace_xtreactor, ace_motif { + exename = XtMotifReactor_Test + + Source_Files { + XtMotifReactor_Test.cpp + } +} + +project(XtAthenaReactor Test) : acetest, ace_xtreactor, ace_athena { + exename = XtAthenaReactor_Test + Source_Files { + XtAthenaReactor_Test.cpp + } +} + +project(FlReactor Test) : acetest, ace_flreactor { + exename = FlReactor_Test + + Source_Files { + FlReactor_Test.cpp + } +} + +project(TkReactor Test) : acetest,ace_tkreactor { + exename = TkReactor_Test + + Source_Files { + TkReactor_Test.cpp + } +} + +project(Codecs Test) : acetest { + avoids += ace_for_tao + exename = Codecs_Test + requires += ace_codecs + + Source_Files { + Codecs_Test.cpp + } +} + +project(Tokens Test) : acetest { + exename = Tokens_Test + requires += ace_token + + Source_Files { + Tokens_Test.cpp + } +} + +project(CDR Array Test) : acetest { + avoids += ace_for_tao + exename = CDR_Array_Test + Source_Files { + CDR_Array_Test.cpp + } +} + +project(Service Config Test) : acetest { + after += Service_Config_DLL + exename = Service_Config_Test + Source_Files { + Service_Config_Test.cpp + } +} + +project(Framework Component Test) : acetest { + after += Framework_Component_DLL + exename = Framework_Component_Test + Source_Files { + Framework_Component_Test.cpp + } +} + +project(FIFO Test) : acetest { + avoids += ace_for_tao + exename = FIFO_Test + Source_Files { + FIFO_Test.cpp + } + Header_Files { + } +} + +project(WFMO Reactor Test) : acetest { + exename = WFMO_Reactor_Test + Source_Files { + WFMO_Reactor_Test.cpp + } + Header_Files { + } +} + +project(NonBlocking Conn Test) : acetest { + exename = NonBlocking_Conn_Test + Source_Files { + NonBlocking_Conn_Test.cpp + } + Header_Files { + } +} + +project(Reference Counted Event Handler Test) : acetest { + exename = Reference_Counted_Event_Handler_Test + Source_Files { + Reference_Counted_Event_Handler_Test.cpp + } + Header_Files { + } +} + +project(MT Ref Counted Event Handler Test) : acetest { + exename = MT_Reference_Counted_Event_Handler_Test + Source_Files { + MT_Reference_Counted_Event_Handler_Test.cpp + } + Header_Files { + } +} + +project(MT Reference Counted Notify Test) : acetest { + exename = MT_Reference_Counted_Notify_Test + Source_Files { + MT_Reference_Counted_Notify_Test.cpp + } + Header_Files { + } +} + +project(Timer Cancellation Test) : acetest { + exename = Timer_Cancellation_Test + Source_Files { + Timer_Cancellation_Test.cpp + } + Header_Files { + } +} + +project(Timer Queue Reference Counting Test) : acetest { + exename = Timer_Queue_Reference_Counting_Test + Source_Files { + Timer_Queue_Reference_Counting_Test.cpp + } + Header_Files { + } +} + +project(Reactor Registration Test) : acetest { + exename = Reactor_Registration_Test + Source_Files { + Reactor_Registration_Test.cpp + } + Header_Files { + } +} + +project(UUID_Test) : acetest { + avoids += ace_for_tao + exename = UUID_Test + requires += ace_uuid + Source_Files { + UUID_Test.cpp + } +} + +project(Unbounded Set Test) : acetest { + exename = Unbounded_Set_Test + Source_Files { + Unbounded_Set_Test.cpp + } +} + +project(INET Addr Test IPV6) : acetest { + exename = INET_Addr_Test_IPV6 + Source_Files { + INET_Addr_Test_IPV6.cpp + } +} + +project(Max Default Port Test IPV6) : acetest { + exename = Max_Default_Port_Test_IPV6 + Source_Files { + Max_Default_Port_Test_IPV6.cpp + } +} + +project(Multicast Test IPV6) : acetest { + exename = Multicast_Test_IPV6 + Source_Files { + Multicast_Test_IPV6.cpp + } +} + +project(Multihomed INET Addr Test IPV6) : acetest { + avoids += ace_for_tao + exename = Multihomed_INET_Addr_Test_IPV6 + Source_Files { + Multihomed_INET_Addr_Test_IPV6.cpp + } +} + +project(Proactor Test IPV6) : acetest { + avoids += ace_for_tao + exename = Proactor_Test_IPV6 + Source_Files { + Proactor_Test_IPV6.cpp + } +} + +project(SOCK Send Recv Test IPV6) : acetest { + exename = SOCK_Send_Recv_Test_IPV6 + Source_Files { + SOCK_Send_Recv_Test_IPV6.cpp + } +} + +project(SOCK Test IPv6) : acetest { + exename = SOCK_Test_IPv6 + Source_Files { + SOCK_Test_IPv6.cpp + } +} + +project(SOCK_Dgram_Bcast_Test) : acetest { + avoids += ace_for_tao + exename = SOCK_Dgram_Bcast_Test + Source_Files { + SOCK_Dgram_Bcast_Test.cpp + } +} + +project(SOCK_SEQPACK_SCTP_Test) : acetest { + avoids += ace_for_tao + exename = SOCK_SEQPACK_SCTP_Test + Source_Files { + SOCK_SEQPACK_SCTP_Test.cpp + } +} + +project(QtReactor Test) : acetest, ace_qtreactor { + exename = QtReactor_Test + MOC_Files { + QtReactor_Test.h + } + Source_Files { + QtReactor_Test_moc.cpp + QtReactor_Test.cpp + } +} + +project(Based Pointer Test) : acetest { + avoids += ace_for_tao + after += Based_Pointer_Test_Lib + exename = Based_Pointer_Test + Source_Files { + Based_Pointer_Test.cpp + } +} + +project(Library Unload): unload_libace { + avoids += wince + exename = UnloadLibACE + staticflags += ACE_AS_STATIC_LIBS + Source_Files { + Unload_libACE.cpp + } + Resource_Files { + } +} + +project(Bug_2659_Regression_Test) : acetest { + exename = Bug_2659_Regression_Test + Source_Files { + Bug_2659_Regression_Test.cpp + } +} + +project(Bug_2609_Regression_Test) : acetest { + avoids += ace_for_tao + exename = Bug_2609_Regression_Test + Source_Files { + Bug_2609_Regression_Test.cpp + } +} + +project(Bug_2610_Regression_Test) : acetest { + avoids += ace_for_tao + exename = Bug_2610_Regression_Test + Source_Files { + Bug_2610_Regression_Test.cpp + } +} diff --git a/ACE/tests/tests.mwc b/ACE/tests/tests.mwc new file mode 100644 index 00000000000..e3eb1548302 --- /dev/null +++ b/ACE/tests/tests.mwc @@ -0,0 +1,9 @@ +// -*- MPC -*- +// $Id$ + +workspace { + exclude { + pharlap + log + } +} diff --git a/ACE/tests/tests_pharlap_msvc.lnk b/ACE/tests/tests_pharlap_msvc.lnk new file mode 100644 index 00000000000..39af043e409 --- /dev/null +++ b/ACE/tests/tests_pharlap_msvc.lnk @@ -0,0 +1,72 @@ +! $Id$ +! Application linker command file for ACE tests. + +! Visual C++ Compiler with PC-Compatible Target +@vc.emb + +! PC-Compatible Screen Driver +!@pcat_sc.emb + +! PC-Compatible Keyboard Driver +!@pcat_kb.emb + +! Multithread Support, Debug version +@vcmtd.emb + +! Structured Exception Handling +@strucexc.emb + +! Floating Point Emulator +!@fpem.emb + +! MS-DOS Compatible File System +!@lfs.emb + +! DLL Loader +!@ldr.emb + +! TCP/IP Support +@winsock.emb + +! TCP/IP Driver +!@eth-smc.emb ! SMC 8003/8216/8416 +!@eth-smc9.emb ! SMC 91C92/91C94 +@eth-3com.emb ! 3Com 3C509 +!@eth-ne2k.emb ! NE2000 +!@eth-dec.emb ! Digital 2114x +!@ppp16550.emb ! PPP 8250/16450/16550 +!@slp16550.emb ! SLIP/CSLIP 8250/16450/16550 + +! MicroWeb Server +!@microweb.emb + +! FTP Server +!@ftpserve.emb + +! Event Logging +!@log.emb + +! PC Card Support +!@pccard.emb + +! PC Card Enablers +!@cs-ser.emb ! Serial Ports +!@cs-ide.emb ! ATA Disk Drives +!@cs-3com.emb ! 3Com Ethernet +!@cs-ne2k.emb ! NE2000 Ethernet + +! M-Systems Flash Support +!@pcfd.emb ! PC-FD +!@dochip2.emb ! DiskOnChip 2000 + +! The section below contains the user-specified switches from the +! Extra Application Link File Switches section of the Extra Linker Switches +! property sheet. +! + +! 32K stack works well. The 8K default is way too small +-stack 32767 + +! Many of the ACE tests are too large to load in the default space without +! running over the ROM area at some point, so go up. +-offset 10_0000h diff --git a/ACE/tests/unload_libace.mpb b/ACE/tests/unload_libace.mpb new file mode 100644 index 00000000000..bbc90e9a707 --- /dev/null +++ b/ACE/tests/unload_libace.mpb @@ -0,0 +1,14 @@ +// -*- MPC -*- +// $Id$ + +feature(ace_for_tao) { + macros += ACE_LIB_NAME=\\"ACE_FOR_TAO\\" +} +project { + specific(vc8, nmake) { + macros += _CRT_SECURE_NO_WARNINGS + } + specific(nmake) { + compile_flags += /wd4996 + } +} |