From ad0434eb2b6f4c300594dffec1defac3ef62baff Mon Sep 17 00:00:00 2001 From: storri Date: Sat, 12 Jan 2002 00:02:33 +0000 Subject: Updates for directory structure --- ace/Streams/Codeset_IBM1047.cpp | 284 +++ ace/Streams/Codeset_IBM1047.h | 112 ++ ace/Streams/Message_Queue.cpp | 442 ++++ ace/Streams/Message_Queue.h | 523 +++++ ace/Streams/Message_Queue.i | 224 +++ ace/Streams/Message_Queue_T.cpp | 1858 +++++++++++++++++ ace/Streams/Message_Queue_T.h | 1053 ++++++++++ ace/Streams/Message_Queue_T.i | 303 +++ ace/Streams/Module.cpp | 266 +++ ace/Streams/Module.h | 207 ++ ace/Streams/Module.i | 62 + ace/Streams/Notification_Strategy.cpp | 18 + ace/Streams/Notification_Strategy.h | 63 + ace/Streams/Notification_Strategy.inl | 26 + ace/Streams/Read_Buffer.cpp | 162 ++ ace/Streams/Read_Buffer.h | 114 ++ ace/Streams/Read_Buffer.i | 28 + ace/Streams/Stream.cpp | 614 ++++++ ace/Streams/Stream.h | 232 +++ ace/Streams/Stream.i | 49 + ace/Streams/Stream_Modules.cpp | 369 ++++ ace/Streams/Stream_Modules.h | 144 ++ ace/Streams/Task.cpp | 227 +++ ace/Streams/Task.h | 265 +++ ace/Streams/Task.i | 114 ++ ace/Streams/Task_T.cpp | 105 + ace/Streams/Task_T.h | 174 ++ ace/Streams/Task_T.i | 103 + ace/Streams/Typed_SV_Message.cpp | 26 + ace/Streams/Typed_SV_Message.h | 94 + ace/Streams/Typed_SV_Message.i | 91 + ace/Streams/Typed_SV_Message_Queue.cpp | 53 + ace/Streams/Typed_SV_Message_Queue.h | 86 + ace/Streams/Typed_SV_Message_Queue.i | 77 + ace/Streams/streams.h | 149 ++ ace/Svcconf/DLL.cpp | 188 ++ ace/Svcconf/DLL.h | 115 ++ ace/Svcconf/Dynamic_Service.cpp | 19 + ace/Svcconf/Dynamic_Service.h | 52 + ace/Svcconf/Dynamic_Service.i | 8 + ace/Svcconf/Dynamic_Service_Base.cpp | 39 + ace/Svcconf/Dynamic_Service_Base.h | 40 + ace/Svcconf/Dynamic_Service_Base.i | 2 + ace/Svcconf/Parse_Node.cpp | 652 ++++++ ace/Svcconf/Parse_Node.h | 343 ++++ ace/Svcconf/Parse_Node.i | 19 + ace/Svcconf/Service_Config.cpp | 884 ++++++++ ace/Svcconf/Service_Config.h | 516 +++++ ace/Svcconf/Service_Config.i | 101 + ace/Svcconf/Service_Manager.cpp | 367 ++++ ace/Svcconf/Service_Manager.h | 120 ++ ace/Svcconf/Service_Manager.i | 10 + ace/Svcconf/Service_Object.cpp | 96 + ace/Svcconf/Service_Object.h | 164 ++ ace/Svcconf/Service_Object.i | 87 + ace/Svcconf/Service_Repository.cpp | 416 ++++ ace/Svcconf/Service_Repository.h | 203 ++ ace/Svcconf/Service_Repository.i | 31 + ace/Svcconf/Service_Templates.cpp | 74 + ace/Svcconf/Service_Templates.h | 29 + ace/Svcconf/Service_Types.cpp | 454 +++++ ace/Svcconf/Service_Types.h | 196 ++ ace/Svcconf/Service_Types.i | 64 + ace/Svcconf/Shared_Object.cpp | 46 + ace/Svcconf/Shared_Object.h | 51 + ace/Svcconf/Shared_Object.i | 9 + ace/Svcconf/Svc_Conf.h | 208 ++ ace/Svcconf/Svc_Conf.l | 140 ++ ace/Svcconf/Svc_Conf.y | 464 +++++ ace/Svcconf/Svc_Conf_Lexer_Guard.cpp | 36 + ace/Svcconf/Svc_Conf_Lexer_Guard.h | 79 + ace/Svcconf/Svc_Conf_Tokens.h | 16 + ace/Svcconf/Svc_Conf_l.cpp | 1841 +++++++++++++++++ ace/Svcconf/Svc_Conf_y.cpp | 1406 +++++++++++++ ace/Svcconf/Svc_Handler.cpp | 512 +++++ ace/Svcconf/Svc_Handler.h | 322 +++ ace/Svcconf/svc_export.h | 44 + ace/Threads/Activation_Queue.cpp | 109 + ace/Threads/Activation_Queue.h | 109 + ace/Threads/Activation_Queue.i | 22 + ace/Threads/Atomic_Op.cpp | 83 + ace/Threads/Atomic_Op.h | 191 ++ ace/Threads/Atomic_Op.i | 219 ++ ace/Threads/File_Lock.cpp | 78 + ace/Threads/File_Lock.h | 164 ++ ace/Threads/File_Lock.inl | 89 + ace/Threads/Makefile | 65 + ace/Threads/Process.cpp | 893 ++++++++ ace/Threads/Process.h | 557 +++++ ace/Threads/Process.i | 379 ++++ ace/Threads/Process_Manager.cpp | 941 +++++++++ ace/Threads/Process_Manager.h | 398 ++++ ace/Threads/Process_Manager.i | 8 + ace/Threads/Process_Mutex.cpp | 76 + ace/Threads/Process_Mutex.h | 195 ++ ace/Threads/Process_Mutex.inl | 85 + ace/Threads/Process_Semaphore.cpp | 91 + ace/Threads/Process_Semaphore.h | 142 ++ ace/Threads/Process_Semaphore.inl | 61 + ace/Threads/RW_Process_Mutex.cpp | 59 + ace/Threads/RW_Process_Mutex.h | 114 ++ ace/Threads/RW_Process_Mutex.inl | 72 + ace/Threads/Synch.cpp | 902 +++++++++ ace/Threads/Synch.h | 1753 ++++++++++++++++ ace/Threads/Synch.h~ | 1753 ++++++++++++++++ ace/Threads/Synch.i | 965 +++++++++ ace/Threads/Synch_Options.cpp | 105 + ace/Threads/Synch_Options.h | 154 ++ ace/Threads/Synch_Options.i | 9 + ace/Threads/Synch_T.cpp | 895 ++++++++ ace/Threads/Synch_T.cpp~ | 895 ++++++++ ace/Threads/Synch_T.h | 903 +++++++++ ace/Threads/Synch_T.h~ | 903 +++++++++ ace/Threads/Synch_T.i | 452 +++++ ace/Threads/Synch_T.i~ | 452 +++++ ace/Threads/Thread.cpp | 90 + ace/Threads/Thread.h | 240 +++ ace/Threads/Thread.i | 272 +++ ace/Threads/Thread_Adapter.cpp | 252 +++ ace/Threads/Thread_Adapter.h | 92 + ace/Threads/Thread_Adapter.inl | 7 + ace/Threads/Thread_Control.cpp | 90 + ace/Threads/Thread_Control.h | 101 + ace/Threads/Thread_Control.inl | 42 + ace/Threads/Thread_Exit.cpp | 144 ++ ace/Threads/Thread_Exit.h | 110 + ace/Threads/Thread_Manager.cpp | 2238 +++++++++++++++++++++ ace/Threads/Thread_Manager.h | 1008 ++++++++++ ace/Threads/Thread_Manager.i | 322 +++ ace/Threads/Token.cpp | 545 +++++ ace/Threads/Token.h | 290 +++ ace/Threads/Token.i | 123 ++ ace/Timer/Basic_Stats.cpp | 73 + ace/Timer/Basic_Stats.h | 87 + ace/Timer/Basic_Stats.inl | 59 + ace/Timer/High_Res_Timer.cpp | 525 +++++ ace/Timer/High_Res_Timer.h | 308 +++ ace/Timer/High_Res_Timer.i | 158 ++ ace/Timer/Profile_Timer.cpp | 418 ++++ ace/Timer/Profile_Timer.h | 133 ++ ace/Timer/Profile_Timer.i | 104 + ace/Timer/System_Time.cpp | 141 ++ ace/Timer/System_Time.h | 87 + ace/Timer/Time_Request_Reply.cpp | 188 ++ ace/Timer/Time_Request_Reply.h | 121 ++ ace/Timer/Time_Value.h | 28 + ace/Timer/Timeprobe.cpp | 44 + ace/Timer/Timeprobe.h | 176 ++ ace/Timer/Timeprobe.i | 8 + ace/Timer/Timeprobe_T.cpp | 300 +++ ace/Timer/Timeprobe_T.h | 188 ++ ace/Timer/Timer_Hash.cpp | 123 ++ ace/Timer/Timer_Hash.h | 71 + ace/Timer/Timer_Hash_T.cpp | 622 ++++++ ace/Timer/Timer_Hash_T.h | 283 +++ ace/Timer/Timer_Heap.cpp | 43 + ace/Timer/Timer_Heap.h | 38 + ace/Timer/Timer_Heap_T.cpp | 785 ++++++++ ace/Timer/Timer_Heap_T.h | 329 +++ ace/Timer/Timer_List.cpp | 44 + ace/Timer/Timer_List.h | 38 + ace/Timer/Timer_List_T.cpp | 342 ++++ ace/Timer/Timer_List_T.h | 217 ++ ace/Timer/Timer_Queue.cpp | 60 + ace/Timer/Timer_Queue.h | 45 + ace/Timer/Timer_Queue_Adapters.cpp | 310 +++ ace/Timer/Timer_Queue_Adapters.h | 230 +++ ace/Timer/Timer_Queue_Adapters.i | 37 + ace/Timer/Timer_Queue_T.cpp | 354 ++++ ace/Timer/Timer_Queue_T.h | 498 +++++ ace/Timer/Timer_Queue_T.i | 188 ++ ace/Timer/Timer_Wheel.cpp | 23 + ace/Timer/Timer_Wheel.h | 39 + ace/Timer/Timer_Wheel_T.cpp | 820 ++++++++ ace/Timer/Timer_Wheel_T.h | 211 ++ ace/Token/Local_Tokens.cpp | 1446 +++++++++++++ ace/Token/Local_Tokens.h | 1098 ++++++++++ ace/Token/Local_Tokens.i | 458 +++++ ace/Token/Remote_Tokens.cpp | 438 ++++ ace/Token/Remote_Tokens.h | 316 +++ ace/Token/Remote_Tokens.i | 122 ++ ace/Token/Token_Collection.cpp | 302 +++ ace/Token/Token_Collection.h | 234 +++ ace/Token/Token_Collection.i | 12 + ace/Token/Token_Invariants.cpp | 368 ++++ ace/Token/Token_Invariants.h | 228 +++ ace/Token/Token_Manager.cpp | 280 +++ ace/Token/Token_Manager.h | 135 ++ ace/Token/Token_Manager.i | 20 + ace/Token/Token_Request_Reply.cpp | 178 ++ ace/Token/Token_Request_Reply.h | 247 +++ ace/Token/Token_Request_Reply.i | 196 ++ ace/Utils/ARGV.cpp | 344 ++++ ace/Utils/ARGV.h | 169 ++ ace/Utils/ARGV.i | 68 + ace/Utils/Active_Map_Manager.cpp | 9 + ace/Utils/Active_Map_Manager.cpp~ | 10 + ace/Utils/Active_Map_Manager.h | 106 + ace/Utils/Active_Map_Manager.h~ | 106 + ace/Utils/Active_Map_Manager.i | 87 + ace/Utils/Active_Map_Manager_T.cpp | 20 + ace/Utils/Active_Map_Manager_T.h | 205 ++ ace/Utils/Active_Map_Manager_T.h~ | 205 ++ ace/Utils/Active_Map_Manager_T.i | 303 +++ ace/Utils/Arg_Shifter.cpp | 203 ++ ace/Utils/Arg_Shifter.h | 192 ++ ace/Utils/Capabilities.cpp | 368 ++++ ace/Utils/Capabilities.h | 199 ++ ace/Utils/Capabilities.i | 47 + ace/Utils/Configuration.cpp | 2055 +++++++++++++++++++ ace/Utils/Configuration.h | 759 +++++++ ace/Utils/Configuration_Import_Export.cpp | 610 ++++++ ace/Utils/Configuration_Import_Export.h | 216 ++ ace/Utils/Connection_Recycling_Strategy.cpp | 13 + ace/Utils/Connection_Recycling_Strategy.h | 67 + ace/Utils/Containers.cpp | 23 + ace/Utils/Containers.h | 72 + ace/Utils/Containers.i | 25 + ace/Utils/Dirent.cpp | 11 + ace/Utils/Dirent.h | 115 ++ ace/Utils/Dirent.i | 95 + ace/Utils/Dirent_Selector.cpp | 44 + ace/Utils/Dirent_Selector.h | 66 + ace/Utils/Dirent_Selector.inl | 15 + ace/Utils/Dynamic.cpp | 39 + ace/Utils/Dynamic.h | 74 + ace/Utils/Dynamic.i | 32 + ace/Utils/Filecache.cpp | 774 +++++++ ace/Utils/Filecache.h | 353 ++++ ace/Utils/Flag_Manip.cpp | 80 + ace/Utils/Flag_Manip.h | 47 + ace/Utils/Flag_Manip.i | 20 + ace/Utils/Functor.cpp | 48 + ace/Utils/Functor.h | 363 ++++ ace/Utils/Functor.i | 188 ++ ace/Utils/Functor_T.cpp | 49 + ace/Utils/Functor_T.h | 152 ++ ace/Utils/Functor_T.i | 28 + ace/Utils/Future.cpp | 448 +++++ ace/Utils/Future.h | 371 ++++ ace/Utils/Future_Set.cpp | 137 ++ ace/Utils/Future_Set.h | 123 ++ ace/Utils/Get_Opt.cpp | 608 ++++++ ace/Utils/Get_Opt.h | 357 ++++ ace/Utils/Get_Opt.i | 34 + ace/Utils/Hash_Map_Manager.cpp | 19 + ace/Utils/Hash_Map_Manager.h | 28 + ace/Utils/Hashable.cpp | 8 + ace/Utils/Hashable.h | 64 + ace/Utils/Hashable.inl | 27 + ace/Utils/IO_Cntl_Msg.cpp | 38 + ace/Utils/IO_Cntl_Msg.h | 95 + ace/Utils/Init_ACE.cpp | 44 + ace/Utils/Init_ACE.h | 65 + ace/Utils/Init_ACE.i | 1 + ace/Utils/Lib_Find.cpp | 562 ++++++ ace/Utils/Lib_Find.h | 106 + ace/Utils/Lib_Find.i | 1 + ace/Utils/Makefile | 89 + ace/Utils/Map.cpp | 19 + ace/Utils/Map.h | 28 + ace/Utils/Message_Block.cpp | 1293 ++++++++++++ ace/Utils/Message_Block.h | 979 +++++++++ ace/Utils/Message_Block.i | 627 ++++++ ace/Utils/Method_Request.cpp | 27 + ace/Utils/Method_Request.h | 66 + ace/Utils/Multiplexor.cpp | 14 + ace/Utils/Multiplexor.h | 81 + ace/Utils/Multiplexor.i | 88 + ace/Utils/NT_Service.cpp | 516 +++++ ace/Utils/NT_Service.h | 398 ++++ ace/Utils/NT_Service.i | 79 + ace/Utils/Object_Manager.cpp | 874 ++++++++ ace/Utils/Object_Manager.h | 480 +++++ ace/Utils/Object_Manager.i | 35 + ace/Utils/Pair.cpp | 19 + ace/Utils/Pair.h | 28 + ace/Utils/Recyclable.cpp | 19 + ace/Utils/Recyclable.h | 78 + ace/Utils/Recyclable.inl | 19 + ace/Utils/Refcountable.cpp | 8 + ace/Utils/Refcountable.h | 58 + ace/Utils/Refcountable.inl | 30 + ace/Utils/Refcounted_Auto_Ptr.h | 192 ++ ace/Utils/Refcounted_Auto_Ptr.i | 222 ++ ace/Utils/Registry.cpp | 1142 +++++++++++ ace/Utils/Registry.h | 560 ++++++ ace/Utils/SString.cpp | 549 +++++ ace/Utils/SString.h | 479 +++++ ace/Utils/SString.i | 284 +++ ace/Utils/Sample_History.cpp | 56 + ace/Utils/Sample_History.h | 86 + ace/Utils/Sample_History.inl | 20 + ace/Utils/Stats.cpp | 612 ++++++ ace/Utils/Stats.h | 270 +++ ace/Utils/Stats.i | 95 + ace/Utils/String_Base_Const.cpp | 5 + ace/Utils/String_Base_Const.h | 32 + ace/Utils/Templates/Array_Base.cpp | 204 ++ ace/Utils/Templates/Array_Base.h | 206 ++ ace/Utils/Templates/Array_Base.inl | 84 + ace/Utils/Templates/Auto_IncDec_T.cpp | 30 + ace/Utils/Templates/Auto_IncDec_T.h | 84 + ace/Utils/Templates/Auto_IncDec_T.i | 21 + ace/Utils/Templates/Auto_Ptr.cpp | 42 + ace/Utils/Templates/Auto_Ptr.h | 172 ++ ace/Utils/Templates/Auto_Ptr.i | 144 ++ ace/Utils/Templates/Cache_Map_Manager_T.cpp | 420 ++++ ace/Utils/Templates/Cache_Map_Manager_T.h | 425 ++++ ace/Utils/Templates/Cache_Map_Manager_T.i | 273 +++ ace/Utils/Templates/Cached_Connect_Strategy_T.cpp | 775 +++++++ ace/Utils/Templates/Cached_Connect_Strategy_T.h | 259 +++ ace/Utils/Templates/Caching_Utility_T.cpp | 500 +++++ ace/Utils/Templates/Caching_Utility_T.h | 343 ++++ ace/Utils/Templates/Cleanup_Strategies_T.cpp | 89 + ace/Utils/Templates/Cleanup_Strategies_T.h | 148 ++ ace/Utils/Templates/Containers_T.cpp | 1813 +++++++++++++++++ ace/Utils/Templates/Containers_T.h | 1495 ++++++++++++++ ace/Utils/Templates/Containers_T.i | 469 +++++ ace/Utils/Templates/Env_Value_T.cpp | 14 + ace/Utils/Templates/Env_Value_T.h | 161 ++ ace/Utils/Templates/Env_Value_T.i | 51 + ace/Utils/Templates/Free_List.cpp | 90 + ace/Utils/Templates/Free_List.h | 148 ++ ace/Utils/Templates/Free_List.i | 76 + ace/Utils/Templates/Hash_Cache_Map_Manager_T.cpp | 230 +++ ace/Utils/Templates/Hash_Cache_Map_Manager_T.h | 215 ++ ace/Utils/Templates/Hash_Cache_Map_Manager_T.i | 68 + ace/Utils/Templates/Hash_Map_Manager_T.cpp | 520 +++++ ace/Utils/Templates/Hash_Map_Manager_T.h | 914 +++++++++ ace/Utils/Templates/Hash_Map_Manager_T.i | 960 +++++++++ ace/Utils/Templates/Hash_Map_With_Allocator_T.cpp | 32 + ace/Utils/Templates/Hash_Map_With_Allocator_T.h | 102 + ace/Utils/Templates/Hash_Map_With_Allocator_T.i | 73 + ace/Utils/Templates/Intrusive_List.cpp | 151 ++ ace/Utils/Templates/Intrusive_List.h | 131 ++ ace/Utils/Templates/Intrusive_List.inl | 19 + ace/Utils/Templates/Intrusive_List_Node.cpp | 25 + ace/Utils/Templates/Intrusive_List_Node.h | 81 + ace/Utils/Templates/Intrusive_List_Node.inl | 25 + ace/Utils/Templates/Managed_Object.cpp | 23 + ace/Utils/Templates/Managed_Object.h | 160 ++ ace/Utils/Templates/Managed_Object.i | 18 + ace/Utils/Templates/Map_Manager.cpp | 692 +++++++ ace/Utils/Templates/Map_Manager.h | 700 +++++++ ace/Utils/Templates/Map_Manager.h~ | 700 +++++++ ace/Utils/Templates/Map_Manager.i | 713 +++++++ ace/Utils/Templates/Map_T.cpp | 18 + ace/Utils/Templates/Map_T.h | 1602 +++++++++++++++ ace/Utils/Templates/Map_T.i | 1723 ++++++++++++++++ ace/Utils/Templates/Message_Block_T.cpp | 46 + ace/Utils/Templates/Message_Block_T.h | 83 + ace/Utils/Templates/Message_Block_T.i | 29 + ace/Utils/Templates/Node.cpp | 46 + ace/Utils/Templates/Node.h | 77 + ace/Utils/Templates/Pair_T.cpp | 18 + ace/Utils/Templates/Pair_T.h | 111 + ace/Utils/Templates/Pair_T.i | 72 + ace/Utils/Templates/RB_Tree.cpp | 1078 ++++++++++ ace/Utils/Templates/RB_Tree.h | 798 ++++++++ ace/Utils/Templates/RB_Tree.i | 1152 +++++++++++ ace/Utils/Templates/Singleton.cpp | 385 ++++ ace/Utils/Templates/Singleton.h | 250 +++ ace/Utils/Templates/Singleton.i | 27 + ace/Utils/Templates/String_Base.cpp | 181 ++ ace/Utils/Templates/String_Base.h | 235 +++ ace/Utils/Templates/String_Base.i | 358 ++++ ace/Utils/Test_and_Set.cpp | 49 + ace/Utils/Test_and_Set.h | 76 + ace/Utils/Test_and_Set.i | 1 + ace/Utils/Unbounded_Queue.cpp | 426 ++++ ace/Utils/Unbounded_Queue.h | 234 +++ ace/Utils/Unbounded_Queue.inl | 21 + ace/Utils/Unbounded_Set.cpp | 443 ++++ ace/Utils/Unbounded_Set.h | 257 +++ ace/Utils/Unbounded_Set.inl | 16 + ace/Utils/libACE_Utils.a | 1 + 377 files changed, 103128 insertions(+) create mode 100644 ace/Streams/Codeset_IBM1047.cpp create mode 100644 ace/Streams/Codeset_IBM1047.h create mode 100644 ace/Streams/Message_Queue.cpp create mode 100644 ace/Streams/Message_Queue.h create mode 100644 ace/Streams/Message_Queue.i create mode 100644 ace/Streams/Message_Queue_T.cpp create mode 100644 ace/Streams/Message_Queue_T.h create mode 100644 ace/Streams/Message_Queue_T.i create mode 100644 ace/Streams/Module.cpp create mode 100644 ace/Streams/Module.h create mode 100644 ace/Streams/Module.i create mode 100644 ace/Streams/Notification_Strategy.cpp create mode 100644 ace/Streams/Notification_Strategy.h create mode 100644 ace/Streams/Notification_Strategy.inl create mode 100644 ace/Streams/Read_Buffer.cpp create mode 100644 ace/Streams/Read_Buffer.h create mode 100644 ace/Streams/Read_Buffer.i create mode 100644 ace/Streams/Stream.cpp create mode 100644 ace/Streams/Stream.h create mode 100644 ace/Streams/Stream.i create mode 100644 ace/Streams/Stream_Modules.cpp create mode 100644 ace/Streams/Stream_Modules.h create mode 100644 ace/Streams/Task.cpp create mode 100644 ace/Streams/Task.h create mode 100644 ace/Streams/Task.i create mode 100644 ace/Streams/Task_T.cpp create mode 100644 ace/Streams/Task_T.h create mode 100644 ace/Streams/Task_T.i create mode 100644 ace/Streams/Typed_SV_Message.cpp create mode 100644 ace/Streams/Typed_SV_Message.h create mode 100644 ace/Streams/Typed_SV_Message.i create mode 100644 ace/Streams/Typed_SV_Message_Queue.cpp create mode 100644 ace/Streams/Typed_SV_Message_Queue.h create mode 100644 ace/Streams/Typed_SV_Message_Queue.i create mode 100644 ace/Streams/streams.h create mode 100644 ace/Svcconf/DLL.cpp create mode 100644 ace/Svcconf/DLL.h create mode 100644 ace/Svcconf/Dynamic_Service.cpp create mode 100644 ace/Svcconf/Dynamic_Service.h create mode 100644 ace/Svcconf/Dynamic_Service.i create mode 100644 ace/Svcconf/Dynamic_Service_Base.cpp create mode 100644 ace/Svcconf/Dynamic_Service_Base.h create mode 100644 ace/Svcconf/Dynamic_Service_Base.i create mode 100644 ace/Svcconf/Parse_Node.cpp create mode 100644 ace/Svcconf/Parse_Node.h create mode 100644 ace/Svcconf/Parse_Node.i create mode 100644 ace/Svcconf/Service_Config.cpp create mode 100644 ace/Svcconf/Service_Config.h create mode 100644 ace/Svcconf/Service_Config.i create mode 100644 ace/Svcconf/Service_Manager.cpp create mode 100644 ace/Svcconf/Service_Manager.h create mode 100644 ace/Svcconf/Service_Manager.i create mode 100644 ace/Svcconf/Service_Object.cpp create mode 100644 ace/Svcconf/Service_Object.h create mode 100644 ace/Svcconf/Service_Object.i create mode 100644 ace/Svcconf/Service_Repository.cpp create mode 100644 ace/Svcconf/Service_Repository.h create mode 100644 ace/Svcconf/Service_Repository.i create mode 100644 ace/Svcconf/Service_Templates.cpp create mode 100644 ace/Svcconf/Service_Templates.h create mode 100644 ace/Svcconf/Service_Types.cpp create mode 100644 ace/Svcconf/Service_Types.h create mode 100644 ace/Svcconf/Service_Types.i create mode 100644 ace/Svcconf/Shared_Object.cpp create mode 100644 ace/Svcconf/Shared_Object.h create mode 100644 ace/Svcconf/Shared_Object.i create mode 100644 ace/Svcconf/Svc_Conf.h create mode 100644 ace/Svcconf/Svc_Conf.l create mode 100644 ace/Svcconf/Svc_Conf.y create mode 100644 ace/Svcconf/Svc_Conf_Lexer_Guard.cpp create mode 100644 ace/Svcconf/Svc_Conf_Lexer_Guard.h create mode 100644 ace/Svcconf/Svc_Conf_Tokens.h create mode 100644 ace/Svcconf/Svc_Conf_l.cpp create mode 100644 ace/Svcconf/Svc_Conf_y.cpp create mode 100644 ace/Svcconf/Svc_Handler.cpp create mode 100644 ace/Svcconf/Svc_Handler.h create mode 100644 ace/Svcconf/svc_export.h create mode 100644 ace/Threads/Activation_Queue.cpp create mode 100644 ace/Threads/Activation_Queue.h create mode 100644 ace/Threads/Activation_Queue.i create mode 100644 ace/Threads/Atomic_Op.cpp create mode 100644 ace/Threads/Atomic_Op.h create mode 100644 ace/Threads/Atomic_Op.i create mode 100644 ace/Threads/File_Lock.cpp create mode 100644 ace/Threads/File_Lock.h create mode 100644 ace/Threads/File_Lock.inl create mode 100644 ace/Threads/Makefile create mode 100644 ace/Threads/Process.cpp create mode 100644 ace/Threads/Process.h create mode 100644 ace/Threads/Process.i create mode 100644 ace/Threads/Process_Manager.cpp create mode 100644 ace/Threads/Process_Manager.h create mode 100644 ace/Threads/Process_Manager.i create mode 100644 ace/Threads/Process_Mutex.cpp create mode 100644 ace/Threads/Process_Mutex.h create mode 100644 ace/Threads/Process_Mutex.inl create mode 100644 ace/Threads/Process_Semaphore.cpp create mode 100644 ace/Threads/Process_Semaphore.h create mode 100644 ace/Threads/Process_Semaphore.inl create mode 100644 ace/Threads/RW_Process_Mutex.cpp create mode 100644 ace/Threads/RW_Process_Mutex.h create mode 100644 ace/Threads/RW_Process_Mutex.inl create mode 100644 ace/Threads/Synch.cpp create mode 100644 ace/Threads/Synch.h create mode 100644 ace/Threads/Synch.h~ create mode 100644 ace/Threads/Synch.i create mode 100644 ace/Threads/Synch_Options.cpp create mode 100644 ace/Threads/Synch_Options.h create mode 100644 ace/Threads/Synch_Options.i create mode 100644 ace/Threads/Synch_T.cpp create mode 100644 ace/Threads/Synch_T.cpp~ create mode 100644 ace/Threads/Synch_T.h create mode 100644 ace/Threads/Synch_T.h~ create mode 100644 ace/Threads/Synch_T.i create mode 100644 ace/Threads/Synch_T.i~ create mode 100644 ace/Threads/Thread.cpp create mode 100644 ace/Threads/Thread.h create mode 100644 ace/Threads/Thread.i create mode 100644 ace/Threads/Thread_Adapter.cpp create mode 100644 ace/Threads/Thread_Adapter.h create mode 100644 ace/Threads/Thread_Adapter.inl create mode 100644 ace/Threads/Thread_Control.cpp create mode 100644 ace/Threads/Thread_Control.h create mode 100644 ace/Threads/Thread_Control.inl create mode 100644 ace/Threads/Thread_Exit.cpp create mode 100644 ace/Threads/Thread_Exit.h create mode 100644 ace/Threads/Thread_Manager.cpp create mode 100644 ace/Threads/Thread_Manager.h create mode 100644 ace/Threads/Thread_Manager.i create mode 100644 ace/Threads/Token.cpp create mode 100644 ace/Threads/Token.h create mode 100644 ace/Threads/Token.i create mode 100644 ace/Timer/Basic_Stats.cpp create mode 100644 ace/Timer/Basic_Stats.h create mode 100644 ace/Timer/Basic_Stats.inl create mode 100644 ace/Timer/High_Res_Timer.cpp create mode 100644 ace/Timer/High_Res_Timer.h create mode 100644 ace/Timer/High_Res_Timer.i create mode 100644 ace/Timer/Profile_Timer.cpp create mode 100644 ace/Timer/Profile_Timer.h create mode 100644 ace/Timer/Profile_Timer.i create mode 100644 ace/Timer/System_Time.cpp create mode 100644 ace/Timer/System_Time.h create mode 100644 ace/Timer/Time_Request_Reply.cpp create mode 100644 ace/Timer/Time_Request_Reply.h create mode 100644 ace/Timer/Time_Value.h create mode 100644 ace/Timer/Timeprobe.cpp create mode 100644 ace/Timer/Timeprobe.h create mode 100644 ace/Timer/Timeprobe.i create mode 100644 ace/Timer/Timeprobe_T.cpp create mode 100644 ace/Timer/Timeprobe_T.h create mode 100644 ace/Timer/Timer_Hash.cpp create mode 100644 ace/Timer/Timer_Hash.h create mode 100644 ace/Timer/Timer_Hash_T.cpp create mode 100644 ace/Timer/Timer_Hash_T.h create mode 100644 ace/Timer/Timer_Heap.cpp create mode 100644 ace/Timer/Timer_Heap.h create mode 100644 ace/Timer/Timer_Heap_T.cpp create mode 100644 ace/Timer/Timer_Heap_T.h create mode 100644 ace/Timer/Timer_List.cpp create mode 100644 ace/Timer/Timer_List.h create mode 100644 ace/Timer/Timer_List_T.cpp create mode 100644 ace/Timer/Timer_List_T.h create mode 100644 ace/Timer/Timer_Queue.cpp create mode 100644 ace/Timer/Timer_Queue.h create mode 100644 ace/Timer/Timer_Queue_Adapters.cpp create mode 100644 ace/Timer/Timer_Queue_Adapters.h create mode 100644 ace/Timer/Timer_Queue_Adapters.i create mode 100644 ace/Timer/Timer_Queue_T.cpp create mode 100644 ace/Timer/Timer_Queue_T.h create mode 100644 ace/Timer/Timer_Queue_T.i create mode 100644 ace/Timer/Timer_Wheel.cpp create mode 100644 ace/Timer/Timer_Wheel.h create mode 100644 ace/Timer/Timer_Wheel_T.cpp create mode 100644 ace/Timer/Timer_Wheel_T.h create mode 100644 ace/Token/Local_Tokens.cpp create mode 100644 ace/Token/Local_Tokens.h create mode 100644 ace/Token/Local_Tokens.i create mode 100644 ace/Token/Remote_Tokens.cpp create mode 100644 ace/Token/Remote_Tokens.h create mode 100644 ace/Token/Remote_Tokens.i create mode 100644 ace/Token/Token_Collection.cpp create mode 100644 ace/Token/Token_Collection.h create mode 100644 ace/Token/Token_Collection.i create mode 100644 ace/Token/Token_Invariants.cpp create mode 100644 ace/Token/Token_Invariants.h create mode 100644 ace/Token/Token_Manager.cpp create mode 100644 ace/Token/Token_Manager.h create mode 100644 ace/Token/Token_Manager.i create mode 100644 ace/Token/Token_Request_Reply.cpp create mode 100644 ace/Token/Token_Request_Reply.h create mode 100644 ace/Token/Token_Request_Reply.i create mode 100644 ace/Utils/ARGV.cpp create mode 100644 ace/Utils/ARGV.h create mode 100644 ace/Utils/ARGV.i create mode 100644 ace/Utils/Active_Map_Manager.cpp create mode 100644 ace/Utils/Active_Map_Manager.cpp~ create mode 100644 ace/Utils/Active_Map_Manager.h create mode 100644 ace/Utils/Active_Map_Manager.h~ create mode 100644 ace/Utils/Active_Map_Manager.i create mode 100644 ace/Utils/Active_Map_Manager_T.cpp create mode 100644 ace/Utils/Active_Map_Manager_T.h create mode 100644 ace/Utils/Active_Map_Manager_T.h~ create mode 100644 ace/Utils/Active_Map_Manager_T.i create mode 100644 ace/Utils/Arg_Shifter.cpp create mode 100644 ace/Utils/Arg_Shifter.h create mode 100644 ace/Utils/Capabilities.cpp create mode 100644 ace/Utils/Capabilities.h create mode 100644 ace/Utils/Capabilities.i create mode 100644 ace/Utils/Configuration.cpp create mode 100644 ace/Utils/Configuration.h create mode 100644 ace/Utils/Configuration_Import_Export.cpp create mode 100644 ace/Utils/Configuration_Import_Export.h create mode 100644 ace/Utils/Connection_Recycling_Strategy.cpp create mode 100644 ace/Utils/Connection_Recycling_Strategy.h create mode 100644 ace/Utils/Containers.cpp create mode 100644 ace/Utils/Containers.h create mode 100644 ace/Utils/Containers.i create mode 100644 ace/Utils/Dirent.cpp create mode 100644 ace/Utils/Dirent.h create mode 100644 ace/Utils/Dirent.i create mode 100644 ace/Utils/Dirent_Selector.cpp create mode 100644 ace/Utils/Dirent_Selector.h create mode 100644 ace/Utils/Dirent_Selector.inl create mode 100644 ace/Utils/Dynamic.cpp create mode 100644 ace/Utils/Dynamic.h create mode 100644 ace/Utils/Dynamic.i create mode 100644 ace/Utils/Filecache.cpp create mode 100644 ace/Utils/Filecache.h create mode 100644 ace/Utils/Flag_Manip.cpp create mode 100644 ace/Utils/Flag_Manip.h create mode 100644 ace/Utils/Flag_Manip.i create mode 100644 ace/Utils/Functor.cpp create mode 100644 ace/Utils/Functor.h create mode 100644 ace/Utils/Functor.i create mode 100644 ace/Utils/Functor_T.cpp create mode 100644 ace/Utils/Functor_T.h create mode 100644 ace/Utils/Functor_T.i create mode 100644 ace/Utils/Future.cpp create mode 100644 ace/Utils/Future.h create mode 100644 ace/Utils/Future_Set.cpp create mode 100644 ace/Utils/Future_Set.h create mode 100644 ace/Utils/Get_Opt.cpp create mode 100644 ace/Utils/Get_Opt.h create mode 100644 ace/Utils/Get_Opt.i create mode 100644 ace/Utils/Hash_Map_Manager.cpp create mode 100644 ace/Utils/Hash_Map_Manager.h create mode 100644 ace/Utils/Hashable.cpp create mode 100644 ace/Utils/Hashable.h create mode 100644 ace/Utils/Hashable.inl create mode 100644 ace/Utils/IO_Cntl_Msg.cpp create mode 100644 ace/Utils/IO_Cntl_Msg.h create mode 100644 ace/Utils/Init_ACE.cpp create mode 100644 ace/Utils/Init_ACE.h create mode 100644 ace/Utils/Init_ACE.i create mode 100644 ace/Utils/Lib_Find.cpp create mode 100644 ace/Utils/Lib_Find.h create mode 100644 ace/Utils/Lib_Find.i create mode 100644 ace/Utils/Makefile create mode 100644 ace/Utils/Map.cpp create mode 100644 ace/Utils/Map.h create mode 100644 ace/Utils/Message_Block.cpp create mode 100644 ace/Utils/Message_Block.h create mode 100644 ace/Utils/Message_Block.i create mode 100644 ace/Utils/Method_Request.cpp create mode 100644 ace/Utils/Method_Request.h create mode 100644 ace/Utils/Multiplexor.cpp create mode 100644 ace/Utils/Multiplexor.h create mode 100644 ace/Utils/Multiplexor.i create mode 100644 ace/Utils/NT_Service.cpp create mode 100644 ace/Utils/NT_Service.h create mode 100644 ace/Utils/NT_Service.i create mode 100644 ace/Utils/Object_Manager.cpp create mode 100644 ace/Utils/Object_Manager.h create mode 100644 ace/Utils/Object_Manager.i create mode 100644 ace/Utils/Pair.cpp create mode 100644 ace/Utils/Pair.h create mode 100644 ace/Utils/Recyclable.cpp create mode 100644 ace/Utils/Recyclable.h create mode 100644 ace/Utils/Recyclable.inl create mode 100644 ace/Utils/Refcountable.cpp create mode 100644 ace/Utils/Refcountable.h create mode 100644 ace/Utils/Refcountable.inl create mode 100644 ace/Utils/Refcounted_Auto_Ptr.h create mode 100644 ace/Utils/Refcounted_Auto_Ptr.i create mode 100644 ace/Utils/Registry.cpp create mode 100644 ace/Utils/Registry.h create mode 100644 ace/Utils/SString.cpp create mode 100644 ace/Utils/SString.h create mode 100644 ace/Utils/SString.i create mode 100644 ace/Utils/Sample_History.cpp create mode 100644 ace/Utils/Sample_History.h create mode 100644 ace/Utils/Sample_History.inl create mode 100644 ace/Utils/Stats.cpp create mode 100644 ace/Utils/Stats.h create mode 100644 ace/Utils/Stats.i create mode 100644 ace/Utils/String_Base_Const.cpp create mode 100644 ace/Utils/String_Base_Const.h create mode 100644 ace/Utils/Templates/Array_Base.cpp create mode 100644 ace/Utils/Templates/Array_Base.h create mode 100644 ace/Utils/Templates/Array_Base.inl create mode 100644 ace/Utils/Templates/Auto_IncDec_T.cpp create mode 100644 ace/Utils/Templates/Auto_IncDec_T.h create mode 100644 ace/Utils/Templates/Auto_IncDec_T.i create mode 100644 ace/Utils/Templates/Auto_Ptr.cpp create mode 100644 ace/Utils/Templates/Auto_Ptr.h create mode 100644 ace/Utils/Templates/Auto_Ptr.i create mode 100644 ace/Utils/Templates/Cache_Map_Manager_T.cpp create mode 100644 ace/Utils/Templates/Cache_Map_Manager_T.h create mode 100644 ace/Utils/Templates/Cache_Map_Manager_T.i create mode 100644 ace/Utils/Templates/Cached_Connect_Strategy_T.cpp create mode 100644 ace/Utils/Templates/Cached_Connect_Strategy_T.h create mode 100644 ace/Utils/Templates/Caching_Utility_T.cpp create mode 100644 ace/Utils/Templates/Caching_Utility_T.h create mode 100644 ace/Utils/Templates/Cleanup_Strategies_T.cpp create mode 100644 ace/Utils/Templates/Cleanup_Strategies_T.h create mode 100644 ace/Utils/Templates/Containers_T.cpp create mode 100644 ace/Utils/Templates/Containers_T.h create mode 100644 ace/Utils/Templates/Containers_T.i create mode 100644 ace/Utils/Templates/Env_Value_T.cpp create mode 100644 ace/Utils/Templates/Env_Value_T.h create mode 100644 ace/Utils/Templates/Env_Value_T.i create mode 100644 ace/Utils/Templates/Free_List.cpp create mode 100644 ace/Utils/Templates/Free_List.h create mode 100644 ace/Utils/Templates/Free_List.i create mode 100644 ace/Utils/Templates/Hash_Cache_Map_Manager_T.cpp create mode 100644 ace/Utils/Templates/Hash_Cache_Map_Manager_T.h create mode 100644 ace/Utils/Templates/Hash_Cache_Map_Manager_T.i create mode 100644 ace/Utils/Templates/Hash_Map_Manager_T.cpp create mode 100644 ace/Utils/Templates/Hash_Map_Manager_T.h create mode 100644 ace/Utils/Templates/Hash_Map_Manager_T.i create mode 100644 ace/Utils/Templates/Hash_Map_With_Allocator_T.cpp create mode 100644 ace/Utils/Templates/Hash_Map_With_Allocator_T.h create mode 100644 ace/Utils/Templates/Hash_Map_With_Allocator_T.i create mode 100644 ace/Utils/Templates/Intrusive_List.cpp create mode 100644 ace/Utils/Templates/Intrusive_List.h create mode 100644 ace/Utils/Templates/Intrusive_List.inl create mode 100644 ace/Utils/Templates/Intrusive_List_Node.cpp create mode 100644 ace/Utils/Templates/Intrusive_List_Node.h create mode 100644 ace/Utils/Templates/Intrusive_List_Node.inl create mode 100644 ace/Utils/Templates/Managed_Object.cpp create mode 100644 ace/Utils/Templates/Managed_Object.h create mode 100644 ace/Utils/Templates/Managed_Object.i create mode 100644 ace/Utils/Templates/Map_Manager.cpp create mode 100644 ace/Utils/Templates/Map_Manager.h create mode 100644 ace/Utils/Templates/Map_Manager.h~ create mode 100644 ace/Utils/Templates/Map_Manager.i create mode 100644 ace/Utils/Templates/Map_T.cpp create mode 100644 ace/Utils/Templates/Map_T.h create mode 100644 ace/Utils/Templates/Map_T.i create mode 100644 ace/Utils/Templates/Message_Block_T.cpp create mode 100644 ace/Utils/Templates/Message_Block_T.h create mode 100644 ace/Utils/Templates/Message_Block_T.i create mode 100644 ace/Utils/Templates/Node.cpp create mode 100644 ace/Utils/Templates/Node.h create mode 100644 ace/Utils/Templates/Pair_T.cpp create mode 100644 ace/Utils/Templates/Pair_T.h create mode 100644 ace/Utils/Templates/Pair_T.i create mode 100644 ace/Utils/Templates/RB_Tree.cpp create mode 100644 ace/Utils/Templates/RB_Tree.h create mode 100644 ace/Utils/Templates/RB_Tree.i create mode 100644 ace/Utils/Templates/Singleton.cpp create mode 100644 ace/Utils/Templates/Singleton.h create mode 100644 ace/Utils/Templates/Singleton.i create mode 100644 ace/Utils/Templates/String_Base.cpp create mode 100644 ace/Utils/Templates/String_Base.h create mode 100644 ace/Utils/Templates/String_Base.i create mode 100644 ace/Utils/Test_and_Set.cpp create mode 100644 ace/Utils/Test_and_Set.h create mode 100644 ace/Utils/Test_and_Set.i create mode 100644 ace/Utils/Unbounded_Queue.cpp create mode 100644 ace/Utils/Unbounded_Queue.h create mode 100644 ace/Utils/Unbounded_Queue.inl create mode 100644 ace/Utils/Unbounded_Set.cpp create mode 100644 ace/Utils/Unbounded_Set.h create mode 100644 ace/Utils/Unbounded_Set.inl create mode 100644 ace/Utils/libACE_Utils.a diff --git a/ace/Streams/Codeset_IBM1047.cpp b/ace/Streams/Codeset_IBM1047.cpp new file mode 100644 index 00000000000..45ae7932665 --- /dev/null +++ b/ace/Streams/Codeset_IBM1047.cpp @@ -0,0 +1,284 @@ +// -*- C++ -*- +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// ace +// +// = FILENAME +// Codeset_IBM1047.cpp +// +// = DESCRIPTION +// Defines the arrays required to convert between ISO8859 (aka +// Latin/1) and IBM1047 (aka EBCDIC). +// +// = AUTHOR +// Jim Rogers (jrogers@viasoft.com) +// +// ============================================================================ + +#include "ace/Codeset_IBM1047.h" + +#if defined(ACE_MVS) + +ACE_RCSID(ace, Codeset_IBM1047, "$Id$") + +// **************************************************************** + +ACE_IBM1047_ISO8859::ACE_IBM1047_ISO8859 (void) +{ +} + +ACE_IBM1047_ISO8859::~ACE_IBM1047_ISO8859 (void) +{ +} + +ACE_CDR::Boolean +ACE_IBM1047_ISO8859::read_char (ACE_InputCDR &in, + ACE_CDR::Char &x) +{ + if (this->read_1 (in, ACE_reinterpret_cast (ACE_CDR::Octet*, &x))) + { + x = ACE_to_IBM1047[x]; + return 1; + } + return 0; +} + +ACE_CDR::Boolean +ACE_IBM1047_ISO8859::read_string (ACE_InputCDR& in, + ACE_CDR::Char *& x) +{ + ACE_CDR::ULong len; + + in.read_ulong (len); + + if (len > 0) + { + ACE_NEW_RETURN (x, + ACE_CDR::Char[len], + 0); + + if (this->read_char_array (in, x, len)) + return 1; + + delete [] x; + } + + x = 0; + return 0; +} + +ACE_CDR::Boolean +ACE_IBM1047_ISO8859::read_char_array (ACE_InputCDR& in, + ACE_CDR::Char* x, + ACE_CDR::ULong len) +{ + if (this->read_array (in, + x, + ACE_CDR::OCTET_SIZE, + ACE_CDR::OCTET_ALIGN, + len)) + { + for (ACE_CDR::ULong i = 0; i != len; ++i) + x[i] = ACE_to_IBM1047[x[i]]; + + return 1; + } + + return 0; +} + +ACE_CDR::Boolean +ACE_IBM1047_ISO8859::write_char (ACE_OutputCDR& out, + ACE_CDR::Char x) +{ + return this->write_1 (out, + ACE_reinterpret_cast (const ACE_CDR::Octet*, + &ACE_from_IBM1047[x])); +} + +ACE_CDR::Boolean +ACE_IBM1047_ISO8859::write_string (ACE_OutputCDR& out, + ACE_CDR::ULong len, + const ACE_CDR::Char* x) +{ + if (out.write_ulong (len + 1)) + return this->write_char_array (out, x, len + 1); + return 0; +} + +ACE_CDR::Boolean +ACE_IBM1047_ISO8859::write_char_array (ACE_OutputCDR& out, + const ACE_CDR::Char* x, + ACE_CDR::ULong len) +{ + char *buf; + if (this->adjust (out, len, 1, buf) == 0) + { + ACE_OS::memcpy (buf, x, len); + + for (ACE_CDR::ULong i = 0; i != len; ++i) + buf[i] = ACE_from_IBM1047[buf[i]]; + + return 1; + } + + this->good_bit(out, 0); + return 0; +} + +// **************************************************************** + +ACE_ISO8859_IBM1047::ACE_ISO8859_IBM1047 (void) +{ +} + +ACE_ISO8859_IBM1047::~ACE_ISO8859_IBM1047 (void) +{ +} + +ACE_CDR::Boolean +ACE_ISO8859_IBM1047::read_char (ACE_InputCDR& in, + ACE_CDR::Char& x) +{ + if (this->read_1 (in, ACE_reinterpret_cast (ACE_CDR::Octet*, &x))) + { + x = ACE_from_IBM1047[x]; + return 1; + } + return 0; +} + +ACE_CDR::Boolean +ACE_ISO8859_IBM1047::read_string (ACE_InputCDR &in, + ACE_CDR::Char *&x) +{ + ACE_CDR::ULong len; + + in.read_ulong (len); + + if (len > 0) + { + ACE_NEW_RETURN (x, + ACE_CDR::Char[len], + 0); + + if (this->read_char_array (in, x, len)) + return 1; + + delete [] x; + } + + x = 0; + return 0; +} + +ACE_CDR::Boolean +ACE_ISO8859_IBM1047::read_char_array (ACE_InputCDR &in, + ACE_CDR::Char *x, + ACE_CDR::ULong len) +{ + if (this->read_array (in, + x, + ACE_CDR::OCTET_SIZE, + ACE_CDR::OCTET_ALIGN, + len)) + { + for (ACE_CDR::ULong i = 0; i != len; ++i) + x[i] = ACE_from_IBM1047[x[i]]; + + return 1; + } + + return 0; +} + +ACE_CDR::Boolean +ACE_ISO8859_IBM1047::write_char (ACE_OutputCDR &out, + ACE_CDR::Char x) +{ + return this->write_1 (out, + ACE_reinterpret_cast (const ACE_CDR::Octet *, + &ACE_to_IBM1047[x])); +} + +ACE_CDR::Boolean +ACE_ISO8859_IBM1047::write_string (ACE_OutputCDR& out, + ACE_CDR::ULong len, + const ACE_CDR::Char* x) +{ + if (out.write_ulong (len + 1)) + return this->write_char_array (out, x, len + 1); + else + return 0; +} + +ACE_CDR::Boolean +ACE_ISO8859_IBM1047::write_char_array (ACE_OutputCDR &out, + const ACE_CDR::Char *x, + ACE_CDR::ULong len) +{ + char *buf; + + if (this->adjust (out, len, 1, buf) == 0) + { + ACE_OS::memcpy (buf, x, len); + + for (ACE_CDR::ULong i = 0; i != len; ++i) + buf[i] = ACE_to_IBM1047[buf[i]]; + + return 1; + } + + this->good_bit (out, 0); + return 0; +} + +// **************************************************************** + +char ACE_to_IBM1047[257] = +{ + "\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x25\x0B\x0C\x0D\x0E\x0F" // 00-0F + "\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x3F\x27\x22\x1D\x35\x1F" // 10-1F + "\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61" // 20-2F + "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F" // 30-3F + "\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6" // 40-4F + "\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D" // 50-5F + "\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96" // 60-6F + "\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x4F\xD0\xA1\x07" // 70-7F + "\x43\x20\x21\x1C\x23\xEB\x24\x9B\x71\x28\x38\x49\x90\xBA\xEC\xDF" // 80-8F + "\x45\x29\x2A\x9D\x72\x2B\x8A\x9A\x67\x56\x64\x4A\x53\x68\x59\x46" // 90-9F + "\xEA\xDA\x2C\xDE\x8B\x55\x41\xFE\x58\x51\x52\x48\x69\xDB\x8E\x8D" // A0-AF + "\x73\x74\x75\xFA\x15\xB0\xB1\xB3\xB4\xB5\x6A\xB7\xB8\xB9\xCC\xBC" // B0-BF + "\xAB\x3E\x3B\x0A\xBF\x8F\x3A\x14\xA0\x17\xCB\xCA\x1A\x1B\x9C\x04" // C0-CF + "\x34\xEF\x1E\x06\x08\x09\x77\x70\xBE\xBB\xAC\x54\x63\x65\x66\x62" // D0-DF + "\x30\x42\x47\x57\xEE\x33\xB6\xE1\xCD\xED\x36\x44\xCE\xCF\x31\xAA" // E0-EF + "\xFC\x9E\xAE\x8C\xDD\xDC\x39\xFB\x80\xAF\xFD\x78\x76\xB2\x9F\xFF" // F0-FF +}; + +char ACE_from_IBM1047[257] = +{ + "\x00\x01\x02\x03\xCF\x09\xD3\x7F\xD4\xD5\xC3\x0B\x0C\x0D\x0E\x0F" // 00-0F + "\x10\x11\x12\x13\xC7\xB4\x08\xC9\x18\x19\xCC\xCD\x83\x1D\xD2\x1F" // 10-1F + "\x81\x82\x1C\x84\x86\x0A\x17\x1B\x89\x91\x92\x95\xA2\x05\x06\x07" // 20-2F + "\x20\xEE\x16\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\xC1\x1A" // 30-3F + "\x20\xA6\xE1\x80\xEB\x90\x9F\xE2\xAB\x8B\x9B\x2E\x3C\x28\x2B\x7C" // 40-4F + "\x26\xA9\xAA\x9C\xDB\xA5\x99\xE3\xA8\x9E\x21\x24\x2A\x29\x3B\x5E" // 50-5F + "\x2D\x2F\xDF\xDC\x9A\xDD\xDE\x98\x9D\xAC\xBA\x2C\x25\x5F\x3E\x3F" // 60-6F + "\xD7\x88\x94\xB0\xB1\xB2\xFC\xD6\xFB\x60\x3A\x23\x40\x27\x3D\x22" // 70-7F + "\xF8\x61\x62\x63\x64\x65\x66\x67\x68\x69\x96\xA4\xF3\xAF\xAE\xC5" // 80-8F + "\x8C\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x97\x87\xCE\x93\xF1\xFE" // 90-9F + "\xC8\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xEF\xC0\xDA\x5B\xF2\xF9" // A0-AF + "\xB5\xB6\xFD\xB7\xB8\xB9\xE6\xBB\xBC\xBD\x8D\xD9\xBF\x5D\xD8\xC4" // B0-BF + "\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCB\xCA\xBE\xE8\xEC\xED" // C0-CF + "\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xA1\xAD\xF5\xF4\xA3\x8F" // D0-DF + "\x5C\xE7\x53\x54\x55\x56\x57\x58\x59\x5A\xA0\x85\x8E\xE9\xE4\xD1" // E0-EF + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xB3\xF7\xF0\xFA\xA7\xFF" // F0-FF +}; + +#elif defined (__HP_aCC) +// Make aC++ stop complaining about an empty translation unit +static int shut_up_aCC = 0; +#endif /* ACE_MVS */ diff --git a/ace/Streams/Codeset_IBM1047.h b/ace/Streams/Codeset_IBM1047.h new file mode 100644 index 00000000000..575b3d2f2e9 --- /dev/null +++ b/ace/Streams/Codeset_IBM1047.h @@ -0,0 +1,112 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Codeset_IBM1047.h + * + * $Id$ + * + * Declares the arrays required to convert between ISO8859 (aka + * Latin/1) and IBM1047 (aka EBCDIC). + * + * + * @author Jim Rogers (jrogers@viasoft.com) + */ +//============================================================================= + + +#ifndef ACE_CODESET_IMB1047_H +#define ACE_CODESET_IMB1047_H +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined(ACE_MVS) + +#include "ace/CDR_Stream.h" + +extern ACE_Export char ACE_to_IBM1047[257]; +extern ACE_Export char ACE_from_IBM1047[257]; + +// **************************************************************** + +/** + * @class ACE_IBM1047_ISO8859 + * + * @brief Codeset translation specialization. + * + * This class performs the codeset translation: + * - Native: IBM_1047 (i.e. EBCDIC) + * - Stream: ISO-8859 (i.e. Latin/1) + */ +class ACE_Export ACE_IBM1047_ISO8859 : public ACE_Char_Codeset_Translator +{ +public: + /// A do nothing constructor. + ACE_IBM1047_ISO8859 (void); + + /// Virtual destruction + virtual ~ACE_IBM1047_ISO8859 (void); + + // = Documented in $ACE_ROOT/ace/CDR_Stream.h + virtual ACE_CDR::Boolean read_char (ACE_InputCDR &, + ACE_CDR::Char &); + virtual ACE_CDR::Boolean read_string (ACE_InputCDR &, + ACE_CDR::Char *&); + virtual ACE_CDR::Boolean read_char_array (ACE_InputCDR &, + ACE_CDR::Char *, + ACE_CDR::ULong); + virtual ACE_CDR::Boolean write_char (ACE_OutputCDR &, + ACE_CDR::Char); + virtual ACE_CDR::Boolean write_string (ACE_OutputCDR &, + ACE_CDR::ULong, + const ACE_CDR::Char *); + virtual ACE_CDR::Boolean write_char_array (ACE_OutputCDR &, + const ACE_CDR::Char *, + ACE_CDR::ULong); +}; + +/** + * @class ACE_ISO8859_IBM1047 + * + * @brief Codeset translation specialization. + * + * This class performs the codeset translation: + * - Native: ISO-8859 (i.e. Latin/1) + * - Stream: IBM-1047 (i.e. EBCDIC) + */ +class ACE_Export ACE_ISO8859_IBM1047 : public ACE_Char_Codeset_Translator +{ +public: + /// A do nothing constructor. + ACE_ISO8859_IBM1047 (void); + + /// Virtual destruction + virtual ~ACE_ISO8859_IBM1047 (void); + + // = Documented in $ACE_ROOT/ace/CDR_Stream.h + virtual ACE_CDR::Boolean read_char (ACE_InputCDR &, + ACE_CDR::Char &); + virtual ACE_CDR::Boolean read_string (ACE_InputCDR &, + ACE_CDR::Char *&); + virtual ACE_CDR::Boolean read_char_array (ACE_InputCDR &, + ACE_CDR::Char *, + ACE_CDR::ULong); + virtual ACE_CDR::Boolean write_char (ACE_OutputCDR &, + ACE_CDR::Char); + virtual ACE_CDR::Boolean write_string (ACE_OutputCDR &, + ACE_CDR::ULong, + const ACE_CDR::Char *); + virtual ACE_CDR::Boolean write_char_array (ACE_OutputCDR &, + const ACE_CDR::Char *, + ACE_CDR::ULong); +}; + +#endif /* ACE_MVS */ + +#include "ace/post.h" +#endif /* ACE_CODESET_IMB1047_H */ diff --git a/ace/Streams/Message_Queue.cpp b/ace/Streams/Message_Queue.cpp new file mode 100644 index 00000000000..bd4189d2f3c --- /dev/null +++ b/ace/Streams/Message_Queue.cpp @@ -0,0 +1,442 @@ +// $Id$ + +#if !defined (ACE_MESSAGE_QUEUE_C) +#define ACE_MESSAGE_QUEUE_C + +#include "ace/Message_Queue.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Message_Queue.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Message_Queue, "$Id$") + +#if defined (VXWORKS) + +//////////////////////////////// +// class ACE_Message_Queue_Vx // +//////////////////////////////// + +void +ACE_Message_Queue_Vx::dump (void) const +{ + ACE_TRACE ("ACE_Message_Queue_Vx::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("deactivated = %d\n") + ACE_LIB_TEXT ("low_water_mark = %d\n") + ACE_LIB_TEXT ("high_water_mark = %d\n") + ACE_LIB_TEXT ("cur_bytes = %d\n") + ACE_LIB_TEXT ("cur_length = %d\n") + ACE_LIB_TEXT ("cur_count = %d\n") + ACE_LIB_TEXT ("head_ = %u\n") + ACE_LIB_TEXT ("MSG_Q_ID = %u\n"), + this->deactivated_, + this->low_water_mark_, + this->high_water_mark_, + this->cur_bytes_, + this->cur_length_, + this->cur_count_, + this->head_, + this->tail_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Message_Queue_Vx::ACE_Message_Queue_Vx (size_t max_messages, + size_t max_message_length, + ACE_Notification_Strategy *ns) + : ACE_Message_Queue (0, 0, ns), + max_messages_ (ACE_static_cast (int, max_messages)), + max_message_length_ (ACE_static_cast (int, max_message_length)) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::ACE_Message_Queue_Vx"); + + if (this->open (max_messages_, max_message_length_, ns) == -1) + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("open"))); +} + +ACE_Message_Queue_Vx::~ACE_Message_Queue_Vx (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::~ACE_Message_Queue_Vx"); + + if (this->tail_ != 0 && this->close () == -1) + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("close"))); +} + +// Don't bother locking since if someone calls this function more than +// once for the same queue, we're in bigger trouble than just +// concurrency control! + +int +ACE_Message_Queue_Vx::open (size_t max_messages, + size_t max_message_length, + ACE_Notification_Strategy *ns) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::open"); + this->high_water_mark_ = 0; + this->low_water_mark_ = 0; + this->deactivated_ = 0; + this->cur_bytes_ = 0; + this->cur_length_ = 0; + this->cur_count_ = 0; + this->head_ = 0; + this->notification_strategy_ = ns; + this->max_messages_ = ACE_static_cast (int, max_messages); + this->max_message_length_ = ACE_static_cast (int, max_message_length); + + if (tail_) + { + // Had already created a msgQ, so delete it. + close (); + activate_i (); + } + + return (this->tail_ = + ACE_reinterpret_cast (ACE_Message_Block *, + ::msgQCreate (max_messages_, + max_message_length_, + MSG_Q_FIFO))) == NULL ? -1 : 0; +} + +// Implementation of the public deactivate() method +// (assumes locks are held). + +int +ACE_Message_Queue_Vx::deactivate_i (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::deactivate_i"); + + int current_status = + this->deactivated_ ? WAS_INACTIVE : WAS_ACTIVE; + + this->deactivated_ = 1; + + return current_status; +} + +int +ACE_Message_Queue_Vx::activate_i (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::activate_i"); + int current_status = + this->deactivated_ ? WAS_INACTIVE : WAS_ACTIVE; + this->deactivated_ = 0; + return current_status; +} + +// Clean up the queue if we have not already done so! + +int +ACE_Message_Queue_Vx::close (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::close"); + // Don't lock, because we don't have a lock. It shouldn't be + // necessary, anyways. + + this->deactivate_i (); + + // Don't bother to free up the remaining message on the list, + // because we don't have any way to iterate over what's in the + // queue. + + return ::msgQDelete (msgq ()); +} + +int +ACE_Message_Queue_Vx::signal_enqueue_waiters (void) +{ + // No-op. + return 0; +} + +int +ACE_Message_Queue_Vx::signal_dequeue_waiters (void) +{ + // No-op. + return 0; +} + +int +ACE_Message_Queue_Vx::enqueue_tail_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::enqueue_tail_i"); + + if (new_item == 0) + return -1; + + // Don't try to send a composite message!!!! Only the first + // block will be sent. + + this->cur_count_++; + + // Always use this method to actually send a message on the queue. + if (::msgQSend (msgq (), + new_item->rd_ptr (), + new_item->size (), + WAIT_FOREVER, + MSG_PRI_NORMAL) == OK) + return ::msgQNumMsgs (msgq ()); + else + return -1; +} + +int +ACE_Message_Queue_Vx::enqueue_head_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::enqueue_head_i"); + + // Just delegate to enqueue_tail_i. + return enqueue_tail_i (new_item); +} + +int +ACE_Message_Queue_Vx::enqueue_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::enqueue_i"); + + if (new_item == 0) + return -1; + + if (this->head_ == 0) + // Should always take this branch. + return this->enqueue_head_i (new_item); + else + ACE_NOTSUP_RETURN (-1); +} + +// Actually get the first ACE_Message_Block (no locking, so must be +// called with locks held). This method assumes that the queue has at +// least one item in it when it is called. + +int +ACE_Message_Queue_Vx::dequeue_head_i (ACE_Message_Block *&first_item) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::dequeue_head_i"); + + // We don't allocate a new Message_Block: the caller must provide + // it, and must ensure that it is big enough (without chaining). + + if (first_item == 0 || first_item->wr_ptr () == 0) + return -1; + + if (::msgQReceive (msgq (), + first_item->wr_ptr (), + first_item->size (), + WAIT_FOREVER) == ERROR) + return -1; + else + return ::msgQNumMsgs (msgq ()); +} + +// Take a look at the first item without removing it. + +int +ACE_Message_Queue_Vx::wait_not_full_cond (ACE_Guard &mon, + ACE_Time_Value *tv) +{ + // Always return here, and let the VxWorks message queue handle blocking. + ACE_UNUSED_ARG (mon); + ACE_UNUSED_ARG (tv); + + return 0; +} + +int +ACE_Message_Queue_Vx::wait_not_empty_cond (ACE_Guard &mon, + ACE_Time_Value *tv) +{ + // Always return here, and let the VxWorks message queue handle blocking. + ACE_UNUSED_ARG (mon); + ACE_UNUSED_ARG (tv); + + return 0; +} + +#if ! defined (ACE_NEEDS_FUNC_DEFINITIONS) +int +ACE_Message_Queue_Vx::peek_dequeue_head (ACE_Message_Block *&, + ACE_Time_Value *tv) +{ + ACE_UNUSED_ARG (tv); + ACE_NOTSUP_RETURN (-1); +} +#endif /* ! ACE_NEEDS_FUNC_DEFINITIONS */ + +#endif /* VXWORKS */ + +#if defined (ACE_WIN32) && (ACE_HAS_WINNT4 != 0) + +ACE_Message_Queue_NT::ACE_Message_Queue_NT (size_t max_threads) + : max_cthrs_ (max_threads), + cur_thrs_ (0), + cur_bytes_ (0), + cur_length_ (0), + cur_count_ (0), + deactivated_ (0), + completion_port_ (ACE_INVALID_HANDLE) +{ + ACE_TRACE ("ACE_Message_Queue_NT::ACE_Message_Queue_NT"); + this->open (max_threads); +} + +int +ACE_Message_Queue_NT::open (size_t max_threads) +{ + ACE_TRACE ("ACE_Message_Queue_NT::open"); + this->max_cthrs_ = max_threads; + this->completion_port_ = ::CreateIoCompletionPort (ACE_INVALID_HANDLE, + NULL, + ACE_Message_Queue_Base::WAS_ACTIVE, + max_threads); + return (this->completion_port_ == NULL ? -1 : 0); +} + +int +ACE_Message_Queue_NT::close (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::close"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + this->deactivate (); + return (::CloseHandle (this->completion_port_) ? 0 : -1 ); +} + +ACE_Message_Queue_NT::~ACE_Message_Queue_NT (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::~ACE_Message_Queue_NT"); + this->close (); +} + +int +ACE_Message_Queue_NT::enqueue (ACE_Message_Block *new_item, + ACE_Time_Value *) +{ + ACE_TRACE ("ACE_Message_Queue_NT::enqueue"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + if (!this->deactivated_) + { + size_t msize = new_item->total_size (); + size_t mlength = new_item->total_length (); + if (::PostQueuedCompletionStatus (this->completion_port_, + msize, + this->deactivated_, + ACE_reinterpret_cast (LPOVERLAPPED, new_item))) + { + // Update the states once I succeed. + this->cur_bytes_ += msize; + this->cur_length_ += mlength; + return ++this->cur_count_; + } + } + else + errno = ESHUTDOWN; + + // Fail to enqueue the message. + return -1; +} + +int +ACE_Message_Queue_NT::dequeue (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_NT::dequeue_head"); + + { + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + if (this->deactivated_) // Make sure the MQ is not deactivated before + { // I proceed. + errno = ESHUTDOWN; // Operation on deactivated MQ not allowed. + return -1; + } + else + ++this->cur_thrs_; // Increase the waiting thread count. + } + + DWORD shutdown; + DWORD msize; + // Get a message from the completion port. + int retv = ::GetQueuedCompletionStatus (this->completion_port_, + &msize, + &shutdown, + ACE_reinterpret_cast (LPOVERLAPPED *, &first_item), + (timeout == 0 ? INFINITE : timeout->msec ())); + { + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + --this->cur_thrs_; // Decrease waiting thread count. + if (retv) + { + if (!shutdown) + { // Really get a valid MB from the queue. + --this->cur_count_; + this->cur_bytes_ -= msize; + this->cur_length_ -= first_item->total_length (); + return this->cur_count_; + } + else // I am woken up by deactivate (). + errno = ESHUTDOWN; + } + } + return -1; +} + +int +ACE_Message_Queue_NT::deactivate (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::deactivate"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + + if (this->deactivated_) // Check if I have been deactivated already. + return ACE_Message_Queue_Base::WAS_INACTIVE; + + this->deactivated_ = 1; + + // Get the number of shutdown messages necessary to wake up + // all waiting threads. + + for (size_t cntr = this->cur_thrs_ - this->cur_count_; + cntr > 0; cntr++) + ::PostQueuedCompletionStatus (this->completion_port_, + 0, + this->deactivated_, + NULL); + return ACE_Message_Queue_Base::WAS_ACTIVE; +} + +int +ACE_Message_Queue_NT::activate (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::activate"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + if (!this->deactivated_) + return ACE_Message_Queue_Base::WAS_ACTIVE; + + this->deactivated_ = 0; + return ACE_Message_Queue_Base::WAS_INACTIVE; +} + +void +ACE_Message_Queue_NT::dump (void) const +{ + ACE_TRACE ("ACE_Message_Queue_NT::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("deactivated = %d\n") + ACE_LIB_TEXT ("max_cthrs_ = %d\n") + ACE_LIB_TEXT ("cur_thrs_ = %d\n") + ACE_LIB_TEXT ("cur_bytes = %d\n") + ACE_LIB_TEXT ("cur_length = %d\n") + ACE_LIB_TEXT ("cur_count = %d\n") + ACE_LIB_TEXT ("completion_port_ = %x\n"), + this->deactivated_, + this->max_cthrs_, + this->cur_thrs_, + this->cur_bytes_, + this->cur_length_, + this->cur_count_, + this->completion_port_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +#endif /* ACE_WIN32 && ACE_HAS_WINNT4 != 0 */ + +#endif /* ACE_MESSAGE_QUEUE_C */ diff --git a/ace/Streams/Message_Queue.h b/ace/Streams/Message_Queue.h new file mode 100644 index 00000000000..ffa2bd77780 --- /dev/null +++ b/ace/Streams/Message_Queue.h @@ -0,0 +1,523 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Message_Queue.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_MESSAGE_QUEUE_H +#define ACE_MESSAGE_QUEUE_H +#include "ace/pre.h" + +#include "ace/Message_Block.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/IO_Cntl_Msg.h" + +// Forward decls. +class ACE_Notification_Strategy; +template class ACE_Message_Queue_Iterator; +template class ACE_Message_Queue_Reverse_Iterator; + +/** + * @class ACE_Message_Queue_Base + * + * @brief Base class for , which is the central + * queueing facility for messages in the ACE framework. + * + * For all the pointer parameters the caller will + * block until action is possible if == 0. Otherwise, it + * will wait until the absolute time specified in * + * elapses. + */ +class ACE_Export ACE_Message_Queue_Base +{ +public: + // = Default high and low water marks. + enum + { + /// Default high watermark (16 K). + DEFAULT_HWM = 16 * 1024, + /// Default low watermark (same as high water mark). + DEFAULT_LWM = 16 * 1024, + /// Message queue was active before or . + WAS_ACTIVE = 1, + /// Message queue was inactive before or . + WAS_INACTIVE = 2 + }; + + ACE_Message_Queue_Base (void); + + /// Close down the message queue and release all resources. + virtual int close (void) = 0; + + /// Close down the message queue and release all resources. + virtual ~ACE_Message_Queue_Base (void); + + // = Enqueue and dequeue methods. + + /** + * Retrieve the first without removing it. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0) = 0; + + /** + * Enqueue a into the tail of the queue. + * Returns number of items in queue if the call succeeds or -1 + * otherwise. These calls return -1 when queue is closed, + * deactivated (in which case == ), when a signal + * occurs (in which case == , or if the time + * specified in timeout elapses (in which case == + * ). + */ + virtual int enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0) = 0; + virtual int enqueue (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0) = 0; + + /** + * Dequeue and return the at the head of the + * queue. Returns number of items in queue if the call succeeds or + * -1 otherwise. These calls return -1 when queue is closed, + * deactivated (in which case == ), when a signal + * occurs (in which case == , or if the time + * specified in timeout elapses (in which case == + * ). + */ + virtual int dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0) = 0; + virtual int dequeue (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0) = 0; + + // = Check if queue is full/empty. + /// True if queue is full, else false. + /// True if queue is empty, else false. + virtual int is_full (void) = 0; + virtual int is_empty (void) = 0; + + // = Queue statistic methods. + + /// Number of total bytes on the queue, i.e., sum of the message + /// block sizes. + virtual size_t message_bytes (void) = 0; + + /// Number of total length on the queue, i.e., sum of the message + /// block lengths. + virtual size_t message_length (void) = 0; + + /// Number of total messages on the queue. + virtual size_t message_count (void) = 0; + + /// New value of the number of total bytes on the queue, i.e., + /// sum of the message block sizes. + virtual void message_bytes (size_t new_size) = 0; + + /// New value of the number of total length on the queue, i.e., + /// sum of the message block lengths. + virtual void message_length (size_t new_length) = 0; + + // = Activation control methods. + + /** + * Deactivate the queue and wakeup all threads waiting on the queue + * so they can continue. No messages are removed from the queue, + * however. Any other operations called until the queue is + * activated again will immediately return -1 with == + * ESHUTDOWN. Returns WAS_INACTIVE if queue was inactive before the + * call and WAS_ACTIVE if queue was active before the call. + */ + virtual int deactivate (void) = 0; + + /** + * Reactivate the queue so that threads can enqueue and dequeue + * messages again. Returns WAS_INACTIVE if queue was inactive + * before the call and WAS_ACTIVE if queue was active before the + * call. + */ + virtual int activate (void) = 0; + + /// Returns true if is enabled. + virtual int deactivated (void) = 0; + + // = Get/set the notification strategy for the + virtual ACE_Notification_Strategy *notification_strategy (void) = 0; + virtual void notification_strategy (ACE_Notification_Strategy *s) = 0; + + // = Notification hook. + + /// Dump the state of an object. + virtual void dump (void) const = 0; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + // = Disallow these operations. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Message_Queue_Base &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Message_Queue_Base (const ACE_Message_Queue_Base &)) +}; + +// Include the templates here. +#include "ace/Message_Queue_T.h" + +#if defined (VXWORKS) +# include /**/ + +/** + * @class ACE_Message_Queue_Vx + * + * @brief Wrapper for VxWorks message queues. + * + * Specialization of ACE_Message_Queue to simply wrap VxWorks + * MsgQ. It does not use any synchronization, because it relies + * on the native MsgQ implementation to take care of that. The + * only system calls that it uses are VxWorks msgQLib calls, so + * it is suitable for use in interrupt service routines. + * NOTE: *Many* ACE_Message_Queue features are not supported with + * this specialization, including: + * * The two size arguments to the constructor and are + * interpreted differently. The first is interpreted as the + * maximum number of bytes in a message. The second is + * interpreted as the maximum number of messages that can be + * queued. + * * *requires* that the ACE_Message_Block + * pointer argument point to an ACE_Message_Block that was + * allocated by the caller. It must be big enough to support + * the received message, without using continutation. The + * pointer argument is not modified. + * * Message priority. MSG_Q_FIFO is hard-coded. + * * enqueue method timeouts. + * * . + * * . + * * The ability to change low and high water marks after creation. + * * chains. The continuation field of + * * is ignored; only the first block of a fragment chain is + * * recognized. + */ +class ACE_Message_Queue_Vx : public ACE_Message_Queue +{ +public: + // = Initialization and termination methods. + ACE_Message_Queue_Vx (size_t max_messages, + size_t max_message_length, + ACE_Notification_Strategy * = 0); + + // Create a message queue with all the defaults. + /// Create a message queue with all the defaults. + virtual int open (size_t max_messages, + size_t max_message_length, + ACE_Notification_Strategy * = 0); + + /// Close down the message queue and release all resources. + virtual int close (void); + + /// Close down the message queue and release all resources. + virtual ~ACE_Message_Queue_Vx (void); + + // = Queue statistic methods. + /** + * Number of total bytes on the queue, i.e., sum of the message + * block sizes. + * Number of total length on the queue, i.e., sum of the message + * block lengths. + * Number of total messages on the queue. + */ + virtual size_t message_bytes (void); + virtual size_t message_length (void); + virtual size_t message_count (void); + + // = Manual changes to these stats (used when queued message blocks + // change size or lengths). + /** + * New value of the number of total bytes on the queue, i.e., sum of + * the message block sizes. + * New value of the number of total length on the queue, i.e., sum + * of the message block lengths. + */ + virtual void message_bytes (size_t new_size); + virtual void message_length (size_t new_length); + + // = Flow control routines + /** + * Get high watermark. + * Set high watermark. + * Get low watermark. + * Set low watermark. + */ + virtual size_t high_water_mark (void); + virtual void high_water_mark (size_t hwm); + virtual size_t low_water_mark (void); + virtual void low_water_mark (size_t lwm); + + // = Activation control methods. + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Enqueue an in accordance with its priority. + virtual int enqueue_i (ACE_Message_Block *new_item); + + /// Enqueue an at the end of the queue. + virtual int enqueue_tail_i (ACE_Message_Block *new_item); + + /// Enqueue an at the head of the queue. + virtual int enqueue_head_i (ACE_Message_Block *new_item); + + /// Dequeue and return the at the head of the + /// queue. + virtual int dequeue_head_i (ACE_Message_Block *&first_item); + + // = Check the boundary conditions (assumes locks are held). + /// True if queue is full, else false. + /// True if queue is empty, else false. + virtual int is_full_i (void); + virtual int is_empty_i (void); + + // = Implementation of public / methods above. + + // These methods assume locks are held. + + /// Deactivate the queue. + /// Activate the queue. + virtual int deactivate_i (void); + virtual int activate_i (void); + + // = Helper methods to factor out common #ifdef code. + /// Wait for the queue to become non-full. + virtual int wait_not_full_cond (ACE_Guard &mon, + ACE_Time_Value *tv); + + /// Wait for the queue to become non-empty. + virtual int wait_not_empty_cond (ACE_Guard &mon, + ACE_Time_Value *tv); + + /// Inform any threads waiting to enqueue that they can procede. + virtual int signal_enqueue_waiters (void); + + /// Inform any threads waiting to dequeue that they can procede. + virtual int signal_dequeue_waiters (void); + + /// Access the underlying msgQ. + MSG_Q_ID msgq (void); + +private: + /// Maximum number of messages that can be queued. + int max_messages_; + + /// Maximum message size, in bytes. + int max_message_length_; + + /// Native message queue options. + int options_; + + // = Disallow these operations. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Message_Queue_Vx &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Message_Queue_Vx (const ACE_Message_Queue_Vx &)) + + ACE_UNIMPLEMENTED_FUNC (virtual int peek_dequeue_head + (ACE_Message_Block *&first_item, + ACE_Time_Value *tv = 0)) +}; +#endif /* VXWORKS */ + +#if defined (ACE_WIN32) && (ACE_HAS_WINNT4 != 0) +/** + * @class ACE_Message_Queue_NT + * + * @brief Message Queue implementation using IO completion port on NT. + * + * Implementation of a strip-downed ACE_Message_Queue using NT's + * IO completion port mechanism. + * NOTE: *Many* ACE_Message_Queue features are not supported with + * this implementation, including: + * * method have different signatures. + * * *requires* that the + * pointer argument point to an that was + * allocated by the caller. + * * . + * * . + * * No flow control. + */ +class ACE_Export ACE_Message_Queue_NT : public ACE_Message_Queue_Base +{ +public: + // = Initialization and termination methods. + ACE_Message_Queue_NT (size_t max_threads = ACE_Message_Queue_Base::DEFAULT_HWM); + + /** + * Initialize the Message Queue by creating a new NT I/O completion + * port. The first arguemnt specifies the number of threads + * released by the MQ that are allowed to run concurrently. Return + * 0 when succeeds, -1 otherwise. + */ + virtual int open (size_t max_threads = ACE_Message_Queue_Base::DEFAULT_HWM); + + /// Close down the underlying I/O completion port. You need to + /// re-open the MQ after this function is executed. + virtual int close (void); + + /// Close down the message queue and release all resources. + virtual ~ACE_Message_Queue_NT (void); + + // = Enqueue and dequeue methods. + + /** + * Enqueue an at the end of the queue. + * Returns -1 on failure, else the number of items still on the + * queue. + */ + virtual int enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + virtual int enqueue (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + /** + * Dequeue and return the at the head of the + * queue. Returns -1 on failure, else the number of items still on + * the queue. + */ + virtual int dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + virtual int dequeue (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + + // = Check if queue is full/empty. + /** + * Always return false. + * True if queue is empty, else false. Notice the return value is + * only transient. + */ + virtual int is_full (void); + virtual int is_empty (void); + + // = Queue statistic methods (transient.) + /** + * Number of total bytes on the queue, i.e., sum of the message + * block sizes. + * Number of total length on the queue, i.e., sum of the message + * block lengths. + * Number of total messages on the queue. + */ + virtual size_t message_bytes (void); + virtual size_t message_length (void); + virtual size_t message_count (void); + + // = Manual changes to these stats (used when queued message blocks + // change size or lengths). + /** + * New value of the number of total bytes on the queue, i.e., sum of + * the message block sizes. + * New value of the number of total length on the queue, i.e., sum + * of the message block lengths. + */ + virtual void message_bytes (size_t new_size); + virtual void message_length (size_t new_length); + + /// Get the max concurrent thread number. + virtual size_t max_threads (void); + + // = Activation control methods. + + /** + * Deactivate the queue and wakeup all threads waiting on the queue + * so they can continue. Messages already in the queue get removed. + * If there are more messages in the queue than there are threads + * waiting on the queue, the left over messages will not be removed. + * Any other enqueue/dequeue operations called until the queue is + * activated again will immediately return -1 with == + * ESHUTDOWN. Returns WAS_INACTIVE if queue was inactive before the + * call and WAS_ACTIVE if queue was active before the call. + */ + virtual int deactivate (void); + + /** + * Reactivate the queue so that threads can enqueue and dequeue + * messages again. Returns WAS_INACTIVE if queue was inactive + * before the call and WAS_ACTIVE if queue was active before the + * call. + */ + virtual int activate (void); + + /// Returns true if is enabled. + virtual int deactivated (void); + + // = Not currently implemented... + int peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + ACE_Notification_Strategy *notification_strategy (void); + void notification_strategy (ACE_Notification_Strategy *s); + + // = Notification hook. + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Get the handle to the underlying completion port. + virtual ACE_HANDLE completion_port (void); + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + // = Internal states. + + /// Maximum threads that can be released (and run) concurrently. + size_t max_cthrs_; + + /// Current number of threads waiting to dequeue messages. + size_t cur_thrs_; + + /// Current number of bytes in queue. + size_t cur_bytes_; + + /// Current length of messages in queue. + size_t cur_length_; + + /// Current number of messages in the queue. + size_t cur_count_; + + /** + * Synchronizer. This should really be an ACE_Recursive_Thread_Mutex + * but since this class is only supported on NT, it's okay to use + * ACE_Thread_Mutex here. + */ + ACE_Thread_Mutex lock_; + + /// Indicates that the queue is inactive. + int deactivated_; + + /// Underlying NT IoCompletionPort. + ACE_HANDLE completion_port_; + + // = Disallow these operations. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Message_Queue_NT &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Message_Queue_NT (const ACE_Message_Queue_NT &)) +}; +#endif /* ACE_WIN32 && ACE_HAS_WINNT4 != 0 */ + + +#if defined (__ACE_INLINE__) +#include "ace/Message_Queue.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_MESSAGE_QUEUE_H */ diff --git a/ace/Streams/Message_Queue.i b/ace/Streams/Message_Queue.i new file mode 100644 index 00000000000..8cf619653a8 --- /dev/null +++ b/ace/Streams/Message_Queue.i @@ -0,0 +1,224 @@ +/* -*- C++ -*- */ +// $Id$ + +ACE_INLINE +ACE_Message_Queue_Base::ACE_Message_Queue_Base (void) +{ +} + +ACE_INLINE +ACE_Message_Queue_Base::~ACE_Message_Queue_Base (void) +{ +} + +#if defined (VXWORKS) +// Specialization to use native VxWorks Message Queues. + +ACE_INLINE MSG_Q_ID +ACE_Message_Queue_Vx::msgq () +{ + // Hijack the tail_ field to store the MSG_Q_ID. + return ACE_reinterpret_cast (MSG_Q_ID, tail_); +} + +ACE_INLINE int +ACE_Message_Queue_Vx::is_empty_i (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::is_empty_i"); + return ::msgQNumMsgs (msgq ()) == 0; +} + +ACE_INLINE int +ACE_Message_Queue_Vx::is_full_i (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::is_full_i"); + return ::msgQNumMsgs (msgq ()) >= max_messages_; +} + +ACE_INLINE size_t +ACE_Message_Queue_Vx::high_water_mark (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::high_water_mark"); + ACE_NOTSUP_RETURN ((size_t) -1); +} + +ACE_INLINE void +ACE_Message_Queue_Vx::high_water_mark (size_t) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::high_water_mark"); + ACE_NOTSUP; +} + +ACE_INLINE size_t +ACE_Message_Queue_Vx::low_water_mark (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::low_water_mark"); + // Don't need to guard, because this is fixed. + + ACE_NOTSUP_RETURN ((size_t) -1); +} + +ACE_INLINE void +ACE_Message_Queue_Vx::low_water_mark (size_t) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::low_water_mark"); + ACE_NOTSUP; +} + +ACE_INLINE size_t +ACE_Message_Queue_Vx::message_bytes (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::message_bytes"); + ACE_NOTSUP_RETURN ((size_t) -1); +} + +ACE_INLINE size_t +ACE_Message_Queue_Vx::message_length (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::message_length"); + ACE_NOTSUP_RETURN ((size_t) -1); +} + +ACE_INLINE size_t +ACE_Message_Queue_Vx::message_count (void) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::message_count"); + // Don't need to guard, because this is a system call. + + return ::msgQNumMsgs (msgq ()); +} + +ACE_INLINE void +ACE_Message_Queue_Vx::message_bytes (size_t) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::message_bytes"); + ACE_NOTSUP; +} + +ACE_INLINE void +ACE_Message_Queue_Vx::message_length (size_t) +{ + ACE_TRACE ("ACE_Message_Queue_Vx::message_length"); + ACE_NOTSUP; +} + +#endif /* VXWORKS */ + +#if defined (ACE_WIN32) && (ACE_HAS_WINNT4 != 0) +ACE_INLINE int +ACE_Message_Queue_NT::enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_NT::enqueue_tail"); + return this->enqueue (new_item, timeout); +} + +ACE_INLINE int +ACE_Message_Queue_NT::dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_NT::dequeue_head"); + return this->dequeue (first_item, timeout); +} + +ACE_INLINE int +ACE_Message_Queue_NT::is_full (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::is_full"); + return 0; // Always not full. +} + +ACE_INLINE int +ACE_Message_Queue_NT::is_empty (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::is_empty"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0); + + return (this->cur_bytes_ > 0 && this->cur_count_ > 0 ? 1 : 0); +} + +ACE_INLINE size_t +ACE_Message_Queue_NT::message_bytes (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::message_bytes"); + // Accessing to size_t must be atomic. + return this->cur_bytes_; +} + +ACE_INLINE size_t +ACE_Message_Queue_NT::message_length (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::message_length"); + // Accessing to size_t must be atomic. + return this->cur_length_; +} + +ACE_INLINE size_t +ACE_Message_Queue_NT::message_count (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::message_count"); + // Accessing to size_t must be atomic. + return this->cur_count_; +} + +ACE_INLINE void +ACE_Message_Queue_NT::message_bytes (size_t new_value) +{ + ACE_TRACE ("ACE_Message_Queue_NT::message_bytes"); + ACE_GUARD (ACE_Thread_Mutex, ace_mon, this->lock_); + + this->cur_bytes_ = new_value; +} + +ACE_INLINE void +ACE_Message_Queue_NT::message_length (size_t new_value) +{ + ACE_TRACE ("ACE_Message_Queue_NT::message_length"); + ACE_GUARD (ACE_Thread_Mutex, ace_mon, this->lock_); + + this->cur_length_ = new_value; +} + +ACE_INLINE size_t +ACE_Message_Queue_NT::max_threads (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::max_threads"); + return this->max_cthrs_; +} + +ACE_INLINE int +ACE_Message_Queue_NT::deactivated (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::deactivated"); + // Accessing to int must be atomic. + return this->deactivated_; +} + +ACE_INLINE ACE_HANDLE +ACE_Message_Queue_NT::completion_port (void) +{ + ACE_TRACE ("ACE_Message_Queue_NT::completion_port"); + return this->completion_port_; +} + +ACE_INLINE int +ACE_Message_Queue_NT::peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_UNUSED_ARG(first_item); + ACE_UNUSED_ARG(timeout); + ACE_NOTSUP_RETURN (-1); +} + +ACE_INLINE ACE_Notification_Strategy * +ACE_Message_Queue_NT::notification_strategy (void) +{ + ACE_NOTSUP_RETURN (0); +} + +ACE_INLINE void +ACE_Message_Queue_NT::notification_strategy (ACE_Notification_Strategy *) +{ +} + +#endif /* ACE_WIN32 && ACE_HAS_WINNT4 != 0 */ diff --git a/ace/Streams/Message_Queue_T.cpp b/ace/Streams/Message_Queue_T.cpp new file mode 100644 index 00000000000..6b7a7dd288a --- /dev/null +++ b/ace/Streams/Message_Queue_T.cpp @@ -0,0 +1,1858 @@ +// $Id$ + +#ifndef ACE_MESSAGE_QUEUE_T_C +#define ACE_MESSAGE_QUEUE_T_C + +// #include Message_Queue.h instead of Message_Queue_T.h to avoid +// circular include problems. +#include "ace/Message_Queue.h" + + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if !defined (__ACE_INLINE__) +#include "ace/Message_Queue_T.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/Notification_Strategy.h" + +ACE_RCSID(ace, Message_Queue_T, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Message_Queue) +ACE_ALLOC_HOOK_DEFINE(ACE_Dynamic_Message_Queue) +ACE_ALLOC_HOOK_DEFINE(ACE_Message_Queue_Ex) + +template void +ACE_Message_Queue_Ex::dump (void) const +{ + ACE_TRACE ("ACE_Message_Queue_Ex::dump"); + + this->queue_.dump (); +} + +template void +ACE_Message_Queue_Ex::message_bytes (size_t new_value) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::message_bytes"); + + this->queue_.message_bytes (new_value); +} + +template void +ACE_Message_Queue_Ex::message_length (size_t new_value) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::message_length"); + + this->queue_.message_length (new_value); +} + +template +ACE_Message_Queue_Ex::ACE_Message_Queue_Ex (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::ACE_Message_Queue_Ex"); + + if (this->queue_.open (hwm, lwm, ns) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_Message_Queue_Ex"))); +} + +template +ACE_Message_Queue_Ex::~ACE_Message_Queue_Ex (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::~ACE_Message_Queue_Ex"); +} + +template int +ACE_Message_Queue_Ex::open (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::open"); + + return this->queue_.open (hwm, lwm, ns); +} + +// Clean up the queue if we have not already done so! + +template int +ACE_Message_Queue_Ex::close (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::close"); + + return this->queue_.close (); +} + +// Take a look at the first item without removing it. + +template int +ACE_Message_Queue_Ex::peek_dequeue_head (ACE_MESSAGE_TYPE *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::peek_dequeue_head"); + + ACE_Message_Block *mb; + + int cur_count = this->queue_.peek_dequeue_head (mb, timeout); + + if (cur_count != -1) + first_item = ACE_reinterpret_cast (ACE_MESSAGE_TYPE *, mb->base ()); + + return cur_count; +} + +template int +ACE_Message_Queue_Ex::enqueue_head (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::enqueue_head"); + + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, + ACE_Message_Block ((char *) new_item, + sizeof (*new_item), + DEFAULT_PRIORITY), + -1); + + int result = this->queue_.enqueue_head (mb, timeout); + if (result == -1) + // Zap the message. + mb->release (); + return result; +} + +// Enqueue an into the in +// accordance with its (0 is lowest priority). Returns +// -1 on failure, else the number of items still on the queue. + +template int +ACE_Message_Queue_Ex::enqueue (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::enqueue_prio"); + + return this->enqueue_prio (new_item, timeout); +} + +template int +ACE_Message_Queue_Ex::enqueue_prio (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::enqueue_prio"); + + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, + ACE_Message_Block ((char *) new_item, + sizeof (*new_item), + DEFAULT_PRIORITY), + -1); + + int result = this->queue_.enqueue_prio (mb, timeout); + if (result == -1) + // Zap the message. + mb->release (); + + return result; +} + +// Block indefinitely waiting for an item to arrive, +// does not ignore alerts (e.g., signals). + +template int +ACE_Message_Queue_Ex::enqueue_tail (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::enqueue_tail"); + + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, + ACE_Message_Block ((char *) new_item, + sizeof (*new_item), + DEFAULT_PRIORITY), + -1); + + int result = this->queue_.enqueue_tail (mb, timeout); + if (result == -1) + // Zap the message. + mb->release (); + return result; +} + +// Remove an item from the front of the queue. If timeout == 0 block +// indefinitely (or until an alert occurs). Otherwise, block for upto +// the amount of time specified by timeout. + +template int +ACE_Message_Queue_Ex::dequeue_head (ACE_MESSAGE_TYPE *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::dequeue_head"); + + ACE_Message_Block *mb; + + int cur_count = this->queue_.dequeue_head (mb, timeout); + + // Dequeue the message. + if (cur_count != -1) + { + first_item = ACE_reinterpret_cast (ACE_MESSAGE_TYPE *, mb->base ()); + // Delete the message block. + mb->release (); + return cur_count; + } + else + return -1; +} + +template int +ACE_Message_Queue_Ex::notify (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::notify"); + + return this->queue_.notify (); +} + +template +ACE_Message_Queue_Iterator::ACE_Message_Queue_Iterator (ACE_Message_Queue &q) + : queue_ (q), + curr_ (q.head_) +{ +} + +template int +ACE_Message_Queue_Iterator::next (ACE_Message_Block *&entry) +{ + ACE_READ_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->queue_.lock_, -1) + + if (this->curr_ != 0) + { + entry = this->curr_; + return 1; + } + + return 0; +} + +template int +ACE_Message_Queue_Iterator::done (void) const +{ + ACE_READ_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->queue_.lock_, -1) + + return this->curr_ == 0; +} + +template int +ACE_Message_Queue_Iterator::advance (void) +{ + ACE_READ_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->queue_.lock_, -1) + + if (this->curr_) + this->curr_ = this->curr_->next (); + return this->curr_ != 0; +} + +template void +ACE_Message_Queue_Iterator::dump (void) const +{ +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Message_Queue_Iterator) + +template +ACE_Message_Queue_Reverse_Iterator::ACE_Message_Queue_Reverse_Iterator (ACE_Message_Queue &q) + : queue_ (q), + curr_ (queue_.tail_) +{ +} + +template int +ACE_Message_Queue_Reverse_Iterator::next (ACE_Message_Block *&entry) +{ + ACE_READ_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->queue_.lock_, -1) + + if (this->curr_ != 0) + { + entry = this->curr_; + return 1; + } + + return 0; +} + +template int +ACE_Message_Queue_Reverse_Iterator::done (void) const +{ + ACE_READ_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->queue_.lock_, -1) + + return this->curr_ == 0; +} + +template int +ACE_Message_Queue_Reverse_Iterator::advance (void) +{ + ACE_READ_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->queue_.lock_, -1) + + if (this->curr_) + this->curr_ = this->curr_->prev (); + return this->curr_ != 0; +} + +template void +ACE_Message_Queue_Reverse_Iterator::dump (void) const +{ +} + +template void +ACE_Message_Queue::dump (void) const +{ + ACE_TRACE ("ACE_Message_Queue::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("deactivated = %d\n") + ACE_LIB_TEXT ("low_water_mark = %d\n") + ACE_LIB_TEXT ("high_water_mark = %d\n") + ACE_LIB_TEXT ("cur_bytes = %d\n") + ACE_LIB_TEXT ("cur_length = %d\n") + ACE_LIB_TEXT ("cur_count = %d\n") + ACE_LIB_TEXT ("head_ = %u\n") + ACE_LIB_TEXT ("tail_ = %u\n"), + this->deactivated_, + this->low_water_mark_, + this->high_water_mark_, + this->cur_bytes_, + this->cur_length_, + this->cur_count_, + this->head_, + this->tail_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("not_full_cond: \n"))); + not_full_cond_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("not_empty_cond: \n"))); + not_empty_cond_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template void +ACE_Message_Queue::message_bytes (size_t new_value) +{ + ACE_TRACE ("ACE_Message_Queue::message_bytes"); + ACE_GUARD (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_); + + this->cur_bytes_ = new_value; +} + +template void +ACE_Message_Queue::message_length (size_t new_value) +{ + ACE_TRACE ("ACE_Message_Queue::message_length"); + ACE_GUARD (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_); + + this->cur_length_ = new_value; +} + +template +ACE_Message_Queue::ACE_Message_Queue (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns) +#if defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + : not_empty_cond_ (0), + not_full_cond_ (0), + enqueue_waiters_ (0), + dequeue_waiters_ (0) +#else + : not_empty_cond_ (this->lock_), + not_full_cond_ (this->lock_) +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ +{ + ACE_TRACE ("ACE_Message_Queue::ACE_Message_Queue"); + + if (this->open (hwm, lwm, ns) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("open"))); +} + +template +ACE_Message_Queue::~ACE_Message_Queue (void) +{ + ACE_TRACE ("ACE_Message_Queue::~ACE_Message_Queue"); + if (this->head_ != 0 && this->close () == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("close"))); +} + +// Don't bother locking since if someone calls this function more than +// once for the same queue, we're in bigger trouble than just +// concurrency control! + +template int +ACE_Message_Queue::open (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns) +{ + ACE_TRACE ("ACE_Message_Queue::open"); + this->high_water_mark_ = hwm; + this->low_water_mark_ = lwm; + this->deactivated_ = 0; + this->cur_bytes_ = 0; + this->cur_length_ = 0; + this->cur_count_ = 0; + this->tail_ = 0; + this->head_ = 0; + this->notification_strategy_ = ns; + return 0; +} + +// Implementation of the public deactivate() method +// (assumes locks are held). + +template int +ACE_Message_Queue::deactivate_i (void) +{ + ACE_TRACE ("ACE_Message_Queue::deactivate_i"); + int current_status = + this->deactivated_ ? WAS_INACTIVE : WAS_ACTIVE; + + // Wakeup all waiters. +#if !defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + this->not_empty_cond_.broadcast (); + this->not_full_cond_.broadcast (); +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + + this->deactivated_ = 1; + return current_status; +} + +template int +ACE_Message_Queue::activate_i (void) +{ + ACE_TRACE ("ACE_Message_Queue::activate_i"); + int current_status = + this->deactivated_ ? WAS_INACTIVE : WAS_ACTIVE; + this->deactivated_ = 0; + return current_status; +} + +// Clean up the queue if we have not already done so! + +template int +ACE_Message_Queue::close (void) +{ + ACE_TRACE ("ACE_Message_Queue::close"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + int result = this->deactivate_i (); + + // Free up the remaining messages on the queue. + + for (this->tail_ = 0; this->head_ != 0; ) + { + this->cur_count_--; + + this->cur_bytes_ -= this->head_->total_size (); + this->cur_length_ -= this->head_->total_length (); + + ACE_Message_Block *temp = this->head_; + this->head_ = this->head_->next (); + + // Make sure to use rather than since this is + // reference counted. + temp->release (); + } + + return result; +} + +template int +ACE_Message_Queue::signal_enqueue_waiters (void) +{ +#if !defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + if (this->not_full_cond_.signal () != 0) + return -1; +#else + if (this->enqueue_waiters_ > 0) + { + --this->enqueue_waiters_; + return this->not_full_cond_.release (); + } +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + return 0; +} + +template int +ACE_Message_Queue::signal_dequeue_waiters (void) +{ +#if !defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + // Tell any blocked threads that the queue has a new item! + if (this->not_empty_cond_.signal () != 0) + return -1; +#else + if (this->dequeue_waiters_ > 0) + { + --this->dequeue_waiters_; + return this->not_empty_cond_.release (); + } +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + return 0; +} + +// Actually put the node at the end (no locking so must be called with +// locks held). + +template int +ACE_Message_Queue::enqueue_tail_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue_tail_i"); + + if (new_item == 0) + return -1; + + // List was empty, so build a new one. + if (this->tail_ == 0) + { + this->head_ = new_item; + this->tail_ = new_item; + new_item->next (0); + new_item->prev (0); + } + // Link at the end. + else + { + new_item->next (0); + this->tail_->next (new_item); + new_item->prev (this->tail_); + this->tail_ = new_item; + } + + // Make sure to count all the bytes in a composite message!!! + this->cur_bytes_ += new_item->total_size (); + this->cur_length_ += new_item->total_length (); + + this->cur_count_++; + + if (this->signal_dequeue_waiters () == -1) + return -1; + else + return this->cur_count_; +} + +// Actually put the node at the head (no locking) + +template int +ACE_Message_Queue::enqueue_head_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue_head_i"); + + if (new_item == 0) + return -1; + + new_item->prev (0); + new_item->next (this->head_); + + if (this->head_ != 0) + this->head_->prev (new_item); + else + this->tail_ = new_item; + + this->head_ = new_item; + + // Make sure to count all the bytes in a composite message!!! + this->cur_bytes_ += new_item->total_size (); + this->cur_length_ += new_item->total_length (); + + this->cur_count_++; + + if (this->signal_dequeue_waiters () == -1) + return -1; + else + return this->cur_count_; +} + +// Actually put the node at its proper position relative to its +// priority. + +template int +ACE_Message_Queue::enqueue_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue_i"); + + if (new_item == 0) + return -1; + + if (this->head_ == 0) + // Check for simple case of an empty queue, where all we need to + // do is insert into the head. + return this->enqueue_head_i (new_item); + else + { + ACE_Message_Block *temp; + + // Figure out where the new item goes relative to its priority. + // We start looking from the highest priority to the lowest + // priority. + + for (temp = this->tail_; + temp != 0; + temp = temp->prev ()) + if (temp->msg_priority () >= new_item->msg_priority ()) + // Break out when we've located an item that has + // greater or equal priority. + break; + + if (temp == 0) + // Check for simple case of inserting at the head of the queue, + // where all we need to do is insert before the + // current head. + return this->enqueue_head_i (new_item); + else if (temp->next () == 0) + // Check for simple case of inserting at the tail of the + // queue, where all we need to do is insert after + // the current tail. + return this->enqueue_tail_i (new_item); + else + { + // Insert the new message behind the message of + // greater or equal priority. This ensures that FIFO order is + // maintained when messages of the same priority are + // inserted consecutively. + new_item->prev (temp); + new_item->next (temp->next ()); + temp->next ()->prev (new_item); + temp->next (new_item); + } + } + + // Make sure to count all the bytes in a composite message!!! + this->cur_bytes_ += new_item->total_size (); + this->cur_length_ += new_item->total_length (); + + this->cur_count_++; + + if (this->signal_dequeue_waiters () == -1) + return -1; + else + return this->cur_count_; +} + +// Actually get the first ACE_Message_Block (no locking, so must be +// called with locks held). This method assumes that the queue has at +// least one item in it when it is called. + +template int +ACE_Message_Queue::dequeue_head_i (ACE_Message_Block *&first_item) +{ + if (this->head_ ==0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Attempting to dequeue from empty queue")), + -1); + ACE_TRACE ("ACE_Message_Queue::dequeue_head_i"); + first_item = this->head_; + this->head_ = this->head_->next (); + + if (this->head_ == 0) + this->tail_ = 0; + else + // The prev pointer of the first message block has to point to + // NULL... + this->head_->prev (0); + + // Subtract off all of the bytes associated with this message. + this->cur_bytes_ -= first_item->total_size (); + this->cur_length_ -= first_item->total_length (); + + this->cur_count_--; + + if (this->cur_count_ == 0 && this->head_ == this->tail_) + this->head_ = this->tail_ = 0; + + // Only signal enqueueing threads if we've fallen below the low + // water mark. + if (this->cur_bytes_ <= this->low_water_mark_ + && this->signal_enqueue_waiters () == -1) + return -1; + else + return this->cur_count_; +} + +// Take a look at the first item without removing it. + +template int +ACE_Message_Queue::peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::peek_dequeue_head"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->deactivated_) + { + errno = ESHUTDOWN; + return -1; + } + + // Wait for at least one item to become available. + + if (this->wait_not_empty_cond (ace_mon, timeout) == -1) + return -1; + + first_item = this->head_; + return this->cur_count_; +} + +template int +ACE_Message_Queue::wait_not_full_cond (ACE_Guard &mon, + ACE_Time_Value *timeout) +{ + int result = 0; +#if defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + while (this->is_full_i () && result != -1) + { + ++this->enqueue_waiters_; + // @@ Need to add sanity checks for failure... + mon.release (); + result = this->not_full_cond_.acquire (timeout); + + if (result == -1 && errno == ETIME) + { + --this->enqueue_waiters_; + errno = EWOULDBLOCK; + } + + // Save/restore errno. + ACE_Errno_Guard error (errno); + mon.acquire (); + } +#else + ACE_UNUSED_ARG (mon); + + // Wait while the queue is full. + + while (this->is_full_i ()) + { + if (this->not_full_cond_.wait (timeout) == -1) + { + if (errno == ETIME) + errno = EWOULDBLOCK; + result = -1; + break; + } + if (this->deactivated_) + { + errno = ESHUTDOWN; + result = -1; + break; + } + } +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + return result; +} + +template int +ACE_Message_Queue::wait_not_empty_cond (ACE_Guard &mon, + ACE_Time_Value *timeout) +{ + int result = 0; +#if defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + while (this->is_empty_i () && result != -1) + { + ++this->dequeue_waiters_; + // @@ Need to add sanity checks for failure... + mon.release (); + result = this->not_empty_cond_.acquire (timeout); + + if (result == -1 && errno == ETIME) + { + --this->dequeue_waiters_; + errno = EWOULDBLOCK; + } + + // Save/restore errno. + ACE_Errno_Guard error (errno); + mon.acquire (); + } +#else + ACE_UNUSED_ARG (mon); + + // Wait while the queue is empty. + + while (this->is_empty_i ()) + { + if (this->not_empty_cond_.wait (timeout) == -1) + { + if (errno == ETIME) + errno = EWOULDBLOCK; + result = -1; + break; + } + if (this->deactivated_) + { + errno = ESHUTDOWN; + result = -1; + break; + } + } +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + return result; +} + +// Block indefinitely waiting for an item to arrive, does not ignore +// alerts (e.g., signals). + +template int +ACE_Message_Queue::enqueue_head (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue_head"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->deactivated_) + { + errno = ESHUTDOWN; + return -1; + } + + if (this->wait_not_full_cond (ace_mon, timeout) == -1) + return -1; + + int queue_count = this->enqueue_head_i (new_item); + + if (queue_count == -1) + return -1; + + this->notify (); + return queue_count; +} + +// Enqueue an into the in +// accordance with its (0 is lowest priority). Returns +// -1 on failure, else the number of items still on the queue. + +template int +ACE_Message_Queue::enqueue_prio (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue_prio"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->deactivated_) + { + errno = ESHUTDOWN; + return -1; + } + + if (this->wait_not_full_cond (ace_mon, timeout) == -1) + return -1; + + int queue_count = this->enqueue_i (new_item); + + if (queue_count == -1) + return -1; + + this->notify (); + return queue_count; +} + +template int +ACE_Message_Queue::enqueue (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue"); + return this->enqueue_prio (new_item, timeout); +} + +// Block indefinitely waiting for an item to arrive, +// does not ignore alerts (e.g., signals). + +template int +ACE_Message_Queue::enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::enqueue_tail"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->deactivated_) + { + errno = ESHUTDOWN; + return -1; + } + + if (this->wait_not_full_cond (ace_mon, timeout) == -1) + return -1; + + int queue_count = this->enqueue_tail_i (new_item); + + if (queue_count == -1) + return -1; + + this->notify (); + return queue_count; +} + +// Remove an item from the front of the queue. If timeout == 0 block +// indefinitely (or until an alert occurs). Otherwise, block for upto +// the amount of time specified by timeout. + +template int +ACE_Message_Queue::dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::dequeue_head"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->deactivated_) + { + errno = ESHUTDOWN; + return -1; + } + + if (this->wait_not_empty_cond (ace_mon, timeout) == -1) + return -1; + + return this->dequeue_head_i (first_item); +} + +template int +ACE_Message_Queue::notify (void) +{ + ACE_TRACE ("ACE_Message_Queue::notify"); + + // By default, don't do anything. + if (this->notification_strategy_ == 0) + return 0; + else + return this->notification_strategy_->notify (); +} + + +// = Initialization and termination methods. +template +ACE_Dynamic_Message_Queue::ACE_Dynamic_Message_Queue (ACE_Dynamic_Message_Strategy & message_strategy, + size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns) + : ACE_Message_Queue (hwm, lwm, ns), + pending_head_ (0), + pending_tail_ (0), + late_head_ (0), + late_tail_ (0), + beyond_late_head_ (0), + beyond_late_tail_ (0), + message_strategy_ (message_strategy) +{ + // Note, the ACE_Dynamic_Message_Queue assumes full responsibility + // for the passed ACE_Dynamic_Message_Strategy object, and deletes + // it in its own dtor +} + +// dtor: free message strategy and let base class dtor do the rest. + +template +ACE_Dynamic_Message_Queue::~ACE_Dynamic_Message_Queue (void) +{ + delete &this->message_strategy_; +} + +template int +ACE_Dynamic_Message_Queue::remove_messages (ACE_Message_Block *&list_head, + ACE_Message_Block *&list_tail, + u_int status_flags) +{ + // start with an empty list + list_head = 0; + list_tail = 0; + + // Get the current time + ACE_Time_Value current_time = ACE_OS::gettimeofday (); + + // Refresh priority status boundaries in the queue. + int result = this->refresh_queue (current_time); + if (result < 0) + return result; + + if (ACE_BIT_ENABLED (status_flags, + (u_int) ACE_Dynamic_Message_Strategy::PENDING) + && this->pending_head_ + && this->pending_tail_) + { + // patch up pointers for the new tail of the queue + if (this->pending_head_->prev ()) + { + this->tail_ = this->pending_head_->prev (); + this->pending_head_->prev ()->next (0); + } + else + { + // the list has become empty + this->head_ = 0; + this->tail_ = 0; + } + + // point to the head and tail of the list + list_head = this->pending_head_; + list_tail = this->pending_tail_; + + // cut the pending messages out of the queue entirely + this->pending_head_->prev (0); + this->pending_head_ = 0; + this->pending_tail_ = 0; + } + + if (ACE_BIT_ENABLED (status_flags, + (u_int) ACE_Dynamic_Message_Strategy::LATE) + && this->late_head_ + && this->late_tail_) + { + // Patch up pointers for the (possibly) new head and tail of the + // queue. + if (this->late_tail_->next ()) + this->late_tail_->next ()->prev (this->late_head_->prev ()); + else + this->tail_ = this->late_head_->prev (); + + if (this->late_head_->prev ()) + this->late_head_->prev ()->next (this->late_tail_->next ()); + else + this->head_ = this->late_tail_->next (); + + // put late messages behind pending messages (if any) being returned + this->late_head_->prev (list_tail); + if (list_tail) + list_tail->next (this->late_head_); + else + list_head = this->late_head_; + + list_tail = this->late_tail_; + + this->late_tail_->next (0); + this->late_head_ = 0; + this->late_tail_ = 0; + } + + if (ACE_BIT_ENABLED (status_flags, + (u_int) ACE_Dynamic_Message_Strategy::BEYOND_LATE) + && this->beyond_late_head_ + && this->beyond_late_tail_) + { + // Patch up pointers for the new tail of the queue + if (this->beyond_late_tail_->next ()) + { + this->head_ = this->beyond_late_tail_->next (); + this->beyond_late_tail_->next ()->prev (0); + } + else + { + // the list has become empty + this->head_ = 0; + this->tail_ = 0; + } + + // Put beyond late messages at the end of the list being + // returned. + if (list_tail) + { + this->beyond_late_head_->prev (list_tail); + list_tail->next (this->beyond_late_head_); + } + else + list_head = this->beyond_late_head_; + + list_tail = this->beyond_late_tail_; + + this->beyond_late_tail_->next (0); + this->beyond_late_head_ = 0; + this->beyond_late_tail_ = 0; + } + + // Decrement message and size counts for removed messages. + ACE_Message_Block *temp1; + + for (temp1 = list_head; + temp1 != 0; + temp1 = temp1->next ()) + { + this->cur_count_--; + + this->cur_bytes_ -= temp1->total_size (); + this->cur_length_ -= temp1->total_length (); + } + + return result; +} + +// Detach all messages with status given in the passed flags from the +// queue and return them by setting passed head and tail pointers to +// the linked list they comprise. This method is intended primarily +// as a means of periodically harvesting messages that have missed +// their deadlines, but is available in its most general form. All +// messages are returned in priority order, from head to tail, as of +// the time this method was called. + +template int +ACE_Dynamic_Message_Queue::dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Dynamic_Message_Queue::dequeue_head"); + + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->deactivated_) + { + errno = ESHUTDOWN; + return -1; + } + + int result; + + // get the current time + ACE_Time_Value current_time = ACE_OS::gettimeofday (); + + // refresh priority status boundaries in the queue + result = this->refresh_queue (current_time); + if (result < 0) + return result; + + // *now* it's appropriate to wait for an enqueued item + result = this->wait_not_empty_cond (ace_mon, timeout); + if (result == -1) + return result; + + // call the internal dequeue method, which selects an item from the + // highest priority status portion of the queue that has messages + // enqueued. + result = this->dequeue_head_i (first_item); + + return result; +} + +// Dequeue and return the at the (logical) head +// of the queue. + +template void +ACE_Dynamic_Message_Queue::dump (void) const +{ + ACE_TRACE ("ACE_Dynamic_Message_Queue::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("ACE_Message_Queue (base class): \n"))); + this->ACE_Message_Queue::dump (); + + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("pending_head_ = %u\n") + ACE_LIB_TEXT ("pending_tail_ = %u\n") + ACE_LIB_TEXT ("late_head_ = %u\n") + ACE_LIB_TEXT ("late_tail_ = %u\n") + ACE_LIB_TEXT ("beyond_late_head_ = %u\n") + ACE_LIB_TEXT ("beyond_late_tail_ = %u\n"), + this->pending_head_, + this->pending_tail_, + this->late_head_, + this->late_tail_, + this->beyond_late_head_, + this->beyond_late_tail_)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("message_strategy_ : \n"))); + message_strategy_.dump (); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + // dump the state of the queue + +template int +ACE_Dynamic_Message_Queue::enqueue_i (ACE_Message_Block *new_item) +{ + ACE_TRACE ("ACE_Dynamic_Message_Queue::enqueue_i"); + + if (new_item == 0) + return -1; + + int result = 0; + + // Get the current time. + ACE_Time_Value current_time = ACE_OS::gettimeofday (); + + // Refresh priority status boundaries in the queue. + + result = this->refresh_queue (current_time); + if (result < 0) + return result; + + // Where we enqueue depends on the message's priority status. + switch (message_strategy_.priority_status (*new_item, + current_time)) + { + case ACE_Dynamic_Message_Strategy::PENDING: + if (this->pending_tail_ == 0) + { + // Check for simple case of an empty pending queue, where + // all we need to do is insert into the tail of + // the queue. + pending_head_ = new_item; + pending_tail_ = pending_head_; + return this->enqueue_tail_i (new_item); + } + else + { + // Enqueue the new message in priority order in the pending + // sublist + result = sublist_enqueue_i (new_item, + current_time, + this->pending_head_, + this->pending_tail_, + ACE_Dynamic_Message_Strategy::PENDING); + } + break; + + case ACE_Dynamic_Message_Strategy::LATE: + if (this->late_tail_ == 0) + { + late_head_ = new_item; + late_tail_ = late_head_; + + if (this->pending_head_ == 0) + // Check for simple case of an empty pending queue, + // where all we need to do is insert into the + // tail of the queue. + return this->enqueue_tail_i (new_item); + else if (this->beyond_late_tail_ == 0) + // Check for simple case of an empty beyond late queue, where all + // we need to do is insert into the head of the queue. + return this->enqueue_head_i (new_item); + else + { + // Otherwise, we can just splice the new message in + // between the pending and beyond late portions of the + // queue. + this->beyond_late_tail_->next (new_item); + new_item->prev (this->beyond_late_tail_); + this->pending_head_->prev (new_item); + new_item->next (this->pending_head_); + } + } + else + { + // Enqueue the new message in priority order in the late + // sublist + result = sublist_enqueue_i (new_item, + current_time, + this->late_head_, + this->late_tail_, + ACE_Dynamic_Message_Strategy::LATE); + } + break; + + case ACE_Dynamic_Message_Strategy::BEYOND_LATE: + if (this->beyond_late_tail_ == 0) + { + // Check for simple case of an empty beyond late queue, + // where all we need to do is insert into the + // head of the queue. + beyond_late_head_ = new_item; + beyond_late_tail_ = beyond_late_head_; + return this->enqueue_head_i (new_item); + } + else + { + // all beyond late messages have the same (zero) priority, + // so just put the new one at the end of the beyond late + // messages + if (this->beyond_late_tail_->next ()) + this->beyond_late_tail_->next ()->prev (new_item); + else + this->tail_ = new_item; + + new_item->next (this->beyond_late_tail_->next ()); + this->beyond_late_tail_->next (new_item); + new_item->prev (this->beyond_late_tail_); + this->beyond_late_tail_ = new_item; + } + + break; + + // should never get here, but just in case... + default: + result = -1; + break; + } + + if (result < 0) + return result; + + this->cur_bytes_ += new_item->total_size (); + this->cur_length_ += new_item->total_length (); + + this->cur_count_++; + + if (this->signal_dequeue_waiters () == -1) + return -1; + else + return this->cur_count_; +} + +// Enqueue an in accordance with its priority. +// priority may be *dynamic* or *static* or a combination or *both* It +// calls the priority evaluation function passed into the Dynamic +// Message Queue constructor to update the priorities of all enqueued +// messages. + +template int +ACE_Dynamic_Message_Queue::sublist_enqueue_i (ACE_Message_Block *new_item, + const ACE_Time_Value ¤t_time, + ACE_Message_Block *&sublist_head, + ACE_Message_Block *&sublist_tail, + ACE_Dynamic_Message_Strategy::Priority_Status status) +{ + int result = 0; + ACE_Message_Block *current_item = 0; + + // Find message after which to enqueue new item, based on message + // priority and priority status. + for (current_item = sublist_tail; + current_item; + current_item = current_item->prev ()) + { + if (message_strategy_.priority_status (*current_item, current_time) == status) + { + if (current_item->msg_priority () >= new_item->msg_priority ()) + break; + } + else + { + sublist_head = new_item; + break; + } + } + + if (current_item == 0) + { + // If the new message has highest priority of any, put it at the + // head of the list (and sublist). + new_item->prev (0); + new_item->next (this->head_); + if (this->head_ != 0) + this->head_->prev (new_item); + else + { + this->tail_ = new_item; + sublist_tail = new_item; + } + this->head_ = new_item; + sublist_head = new_item; + } + else + { + // insert the new item into the list + new_item->next (current_item->next ()); + new_item->prev (current_item); + + if (current_item->next ()) + current_item->next ()->prev (new_item); + else + this->tail_ = new_item; + + current_item->next (new_item); + + // If the new item has lowest priority of any in the sublist, + // move the tail pointer of the sublist back to the new item + if (current_item == sublist_tail) + sublist_tail = new_item; + } + + return result; +} + +// Enqueue a message in priority order within a given priority status +// sublist. + +template int +ACE_Dynamic_Message_Queue::dequeue_head_i (ACE_Message_Block *&first_item) +{ + ACE_TRACE ("ACE_Dynamic_Message_Queue::dequeue_head_i"); + + int result = 0; + int last_in_subqueue = 0; + + // first, try to dequeue from the head of the pending list + if (this->pending_head_) + { + first_item = this->pending_head_; + + if (0 == this->pending_head_->prev ()) + this->head_ = this->pending_head_->next (); + else + this->pending_head_->prev ()->next (this->pending_head_->next ()); + + if (0 == this->pending_head_->next ()) + { + this->tail_ = this->pending_head_->prev (); + this->pending_head_ = 0; + this->pending_tail_ = 0; + } + else + { + this->pending_head_->next ()->prev (this->pending_head_->prev ()); + this->pending_head_ = this->pending_head_->next (); + } + + first_item->prev (0); + first_item->next (0); + } + + // Second, try to dequeue from the head of the late list + else if (this->late_head_) + { + last_in_subqueue = this->late_head_ == this->late_tail_ ? 1 : 0; + + first_item = this->late_head_; + + if (0 == this->late_head_->prev ()) + this->head_ = this->late_head_->next (); + else + this->late_head_->prev ()->next (this->late_head_->next ()); + + if (0 == this->late_head_->next ()) + this->tail_ = this->late_head_->prev (); + else + { + this->late_head_->next ()->prev (this->late_head_->prev ()); + this->late_head_ = this->late_head_->next (); + } + + if (last_in_subqueue) + { + this->late_head_ = 0; + this->late_tail_ = 0; + } + + first_item->prev (0); + first_item->next (0); + } + // finally, try to dequeue from the head of the beyond late list + else if (this->beyond_late_head_) + { + last_in_subqueue = + (this->beyond_late_head_ == this->beyond_late_tail_) ? 1 : 0; + + first_item = this->beyond_late_head_; + this->head_ = this->beyond_late_head_->next (); + + if (0 == this->beyond_late_head_->next ()) + this->tail_ = this->beyond_late_head_->prev (); + else + { + this->beyond_late_head_->next ()->prev (this->beyond_late_head_->prev ()); + this->beyond_late_head_ = this->beyond_late_head_->next (); + } + + if (last_in_subqueue) + { + this->beyond_late_head_ = 0; + this->beyond_late_tail_ = 0; + } + + first_item->prev (0); + first_item->next (0); + } + else + { + // nothing to dequeue: set the pointer to zero and return an error code + first_item = 0; + result = -1; + } + + if (result < 0) + return result; + + // Make sure to subtract off all of the bytes associated with this + // message. + this->cur_bytes_ -= first_item->total_size (); + this->cur_length_ -= first_item->total_length (); + + this->cur_count_--; + + // Only signal enqueueing threads if we've fallen below the low + // water mark. + if (this->cur_bytes_ <= this->low_water_mark_ + && this->signal_enqueue_waiters () == -1) + return -1; + else + return this->cur_count_; +} + +// Dequeue and return the at the head of the +// logical queue. Attempts first to dequeue from the pending portion +// of the queue, or if that is empty from the late portion, or if that +// is empty from the beyond late portion, or if that is empty just +// sets the passed pointer to zero and returns -1. + +template int +ACE_Dynamic_Message_Queue::refresh_queue (const ACE_Time_Value ¤t_time) +{ + int result; + + result = refresh_pending_queue (current_time); + + if (result != -1) + result = refresh_late_queue (current_time); + + return result; +} + +// Refresh the queue using the strategy specific priority status +// function. + +template int +ACE_Dynamic_Message_Queue::refresh_pending_queue (const ACE_Time_Value ¤t_time) +{ + ACE_Dynamic_Message_Strategy::Priority_Status current_status; + + // refresh priority status boundaries in the queue + if (this->pending_head_) + { + current_status = message_strategy_.priority_status (*this->pending_head_, + current_time); + switch (current_status) + { + case ACE_Dynamic_Message_Strategy::BEYOND_LATE: + // Make sure the head of the beyond late queue is set (there + // may not have been any beyond late messages previously) + this->beyond_late_head_ = this->head_; + + // Zero out the late queue pointers, and set them only if + // there turn out to be late messages in the pending sublist + this->late_head_ = 0; + this->late_tail_ = 0; + + // Advance through the beyond late messages in the pending queue + do + { + this->pending_head_ = this->pending_head_->next (); + + if (this->pending_head_) + current_status = message_strategy_.priority_status (*this->pending_head_, + current_time); + else + break; // do while + + } + while (current_status == ACE_Dynamic_Message_Strategy::BEYOND_LATE); + + if (this->pending_head_) + { + // point tail of beyond late sublist to previous item + this->beyond_late_tail_ = this->pending_head_->prev (); + + if (current_status == ACE_Dynamic_Message_Strategy::PENDING) + // there are no late messages left in the queue + break; // switch + else if (current_status != ACE_Dynamic_Message_Strategy::LATE) + { + // if we got here, something is *seriously* wrong with the queue + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Unexpected message priority status [%d] (expected LATE)"), + (int) current_status), + -1); + } + /* FALLTHRU */ + } + else + { + // There are no pending or late messages left in the + // queue. + this->beyond_late_tail_ = this->tail_; + this->pending_head_ = 0; + this->pending_tail_ = 0; + break; // switch + } + + case ACE_Dynamic_Message_Strategy::LATE: + // Make sure the head of the late queue is set (there may + // not have been any late messages previously, or they may + // have all become beyond late). + if (this->late_head_ == 0) + this->late_head_ = this->pending_head_; + + // advance through the beyond late messages in the pending queue + do + { + this->pending_head_ = this->pending_head_->next (); + + if (this->pending_head_) + current_status = message_strategy_.priority_status (*this->pending_head_, + current_time); + else + break; // do while + + } + while (current_status == ACE_Dynamic_Message_Strategy::LATE); + + if (this->pending_head_) + { + if (current_status != ACE_Dynamic_Message_Strategy::PENDING) + // if we got here, something is *seriously* wrong with the queue + ACE_ERROR_RETURN((LM_ERROR, + ACE_LIB_TEXT ("Unexpected message priority status [%d] (expected PENDING)"), + (int) current_status), + -1); + + // Point tail of late sublist to previous item + this->late_tail_ = this->pending_head_->prev (); + } + else + { + // there are no pending messages left in the queue + this->late_tail_ = this->tail_; + this->pending_head_ = 0; + this->pending_tail_ = 0; + } + + break; // switch + case ACE_Dynamic_Message_Strategy::PENDING: + // do nothing - the pending queue is unchanged + break; // switch + default: + // if we got here, something is *seriously* wrong with the queue + ACE_ERROR_RETURN((LM_ERROR, + ACE_LIB_TEXT ("Unknown message priority status [%d]"), + (int) current_status), + -1); + } + } + return 0; +} + +// Refresh the pending queue using the strategy specific priority +// status function. + +template int +ACE_Dynamic_Message_Queue::refresh_late_queue (const ACE_Time_Value ¤t_time) +{ + ACE_Dynamic_Message_Strategy::Priority_Status current_status; + + if (this->late_head_) + { + current_status = message_strategy_.priority_status (*this->late_head_, + current_time); + switch (current_status) + { + case ACE_Dynamic_Message_Strategy::BEYOND_LATE: + + // make sure the head of the beyond late queue is set + // (there may not have been any beyond late messages previously) + this->beyond_late_head_ = this->head_; + + // advance through the beyond late messages in the late queue + do + { + this->late_head_ = this->late_head_->next (); + + if (this->late_head_) + current_status = message_strategy_.priority_status (*this->late_head_, + current_time); + else + break; // do while + + } + while (current_status == ACE_Dynamic_Message_Strategy::BEYOND_LATE); + + if (this->late_head_) + { + // point tail of beyond late sublist to previous item + this->beyond_late_tail_ = this->late_head_->prev (); + + if (current_status == ACE_Dynamic_Message_Strategy::PENDING) + { + // there are no late messages left in the queue + this->late_head_ = 0; + this->late_tail_ = 0; + } + else if (current_status != ACE_Dynamic_Message_Strategy::LATE) + // if we got here, something is *seriously* wrong with the queue + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Unexpected message priority status [%d] (expected LATE)"), + (int) current_status), + -1); + } + else + { + // there are no late messages left in the queue + this->beyond_late_tail_ = this->tail_; + this->late_head_ = 0; + this->late_tail_ = 0; + } + + break; // switch + + case ACE_Dynamic_Message_Strategy::LATE: + // do nothing - the late queue is unchanged + break; // switch + + case ACE_Dynamic_Message_Strategy::PENDING: + // if we got here, something is *seriously* wrong with the queue + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Unexpected message priority status ") + ACE_LIB_TEXT ("[%d] (expected LATE or BEYOND_LATE)"), + (int) current_status), + -1); + default: + // if we got here, something is *seriously* wrong with the queue + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Unknown message priority status [%d]"), + (int) current_status), + -1); + } + } + + return 0; +} + +// Refresh the late queue using the strategy specific priority status +// function. + +template int +ACE_Dynamic_Message_Queue::peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + return ACE_Message_Queue::peek_dequeue_head (first_item, + timeout); +} + +// Private method to hide public base class method: just calls base +// class method. + +template int +ACE_Dynamic_Message_Queue::enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Dynamic_Message_Queue::enqueue_tail"); + return this->enqueue_prio (new_item, timeout); +} + +// Just call priority enqueue method: tail enqueue semantics for +// dynamic message queues are unstable: the message may or may not be +// where it was placed after the queue is refreshed prior to the next +// enqueue or dequeue operation. + +template int +ACE_Dynamic_Message_Queue::enqueue_head (ACE_Message_Block *new_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Dynamic_Message_Queue::enqueue_head"); + return this->enqueue_prio (new_item, timeout); +} + +// Just call priority enqueue method: head enqueue semantics for +// dynamic message queues are unstable: the message may or may not be +// where it was placed after the queue is refreshed prior to the next +// enqueue or dequeue operation. + +template +ACE_Message_Queue * +ACE_Message_Queue_Factory::create_static_message_queue (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns) +{ + ACE_Message_Queue *tmp; + + ACE_NEW_RETURN (tmp, + ACE_Message_Queue (hwm, lwm, ns), + 0); + return tmp; +} + +// Factory method for a statically prioritized ACE_Message_Queue. + +template +ACE_Dynamic_Message_Queue * +ACE_Message_Queue_Factory::create_deadline_message_queue (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns, + u_long static_bit_field_mask, + u_long static_bit_field_shift, + u_long dynamic_priority_max, + u_long dynamic_priority_offset) +{ + ACE_Deadline_Message_Strategy *adms; + + ACE_NEW_RETURN (adms, + ACE_Deadline_Message_Strategy (static_bit_field_mask, + static_bit_field_shift, + dynamic_priority_max, + dynamic_priority_offset), + 0); + + ACE_Dynamic_Message_Queue *tmp; + ACE_NEW_RETURN (tmp, + ACE_Dynamic_Message_Queue (*adms, hwm, lwm, ns), + 0); + return tmp; +} + +// Factory method for a dynamically prioritized (by time to deadline) +// ACE_Dynamic_Message_Queue. + +template +ACE_Dynamic_Message_Queue * +ACE_Message_Queue_Factory::create_laxity_message_queue (size_t hwm, + size_t lwm, + ACE_Notification_Strategy *ns, + u_long static_bit_field_mask, + u_long static_bit_field_shift, + u_long dynamic_priority_max, + u_long dynamic_priority_offset) +{ + ACE_Laxity_Message_Strategy *alms; + + ACE_NEW_RETURN (alms, + ACE_Laxity_Message_Strategy (static_bit_field_mask, + static_bit_field_shift, + dynamic_priority_max, + dynamic_priority_offset), + 0); + + ACE_Dynamic_Message_Queue *tmp; + ACE_NEW_RETURN (tmp, + ACE_Dynamic_Message_Queue (*alms, hwm, lwm, ns), + 0); + return tmp; +} + +// Factory method for a dynamically prioritized (by laxity) +// . + +#if defined (VXWORKS) + +template +ACE_Message_Queue_Vx * +ACE_Message_Queue_Factory::create_Vx_message_queue (size_t max_messages, + size_t max_message_length, + ACE_Notification_Strategy *ns) +{ + ACE_Message_Queue_Vx *tmp; + + ACE_NEW_RETURN (tmp, + ACE_Message_Queue_Vx (max_messages, max_message_length, ns), + 0); + return tmp; +} + // factory method for a wrapped VxWorks message queue + +#if defined (ACE_WIN32) && (ACE_HAS_WINNT4 != 0) + +template +ACE_Message_Queue_NT * +ACE_Message_Queue_Factory::create_NT_message_queue (size_t max_threads) +{ + ACE_Message_Queue_NT *tmp; + + ACE_NEW_RETURN (tmp, + ACE_Message_Queue_NT (max_threads); + 0); + return tmp; +} + +#endif /* ACE_WIN32 && ACE_HAS_WINNT4 != 0 */ +#endif /* defined (VXWORKS) */ +#endif /* ACE_MESSAGE_QUEUE_T_C */ diff --git a/ace/Streams/Message_Queue_T.h b/ace/Streams/Message_Queue_T.h new file mode 100644 index 00000000000..f8155eeed2c --- /dev/null +++ b/ace/Streams/Message_Queue_T.h @@ -0,0 +1,1053 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Message_Queue_T.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_MESSAGE_QUEUE_T_H +#define ACE_MESSAGE_QUEUE_T_H +#include "ace/pre.h" + +#include "ace/Message_Queue.h" +#include "ace/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (VXWORKS) +class ACE_Message_Queue_Vx; +#endif /* defined (VXWORKS) */ + +#if defined (ACE_WIN32) && (ACE_HAS_WINNT4 != 0) +class ACE_Message_Queue_NT; +#endif /* ACE_WIN32 && ACE_HAS_WINNT4 != 0 */ + +/** + * @class ACE_Message_Queue + * + * @brief A threaded message queueing facility, modeled after the + * queueing facilities in System V STREAMs. + * + * An is the central queueing facility for + * messages in the ACE framework. If is + * then all operations are thread-safe. + * Otherwise, if it's then there's no locking + * overhead. + */ +template +class ACE_Message_Queue : public ACE_Message_Queue_Base +{ +public: + friend class ACE_Message_Queue_Iterator; + friend class ACE_Message_Queue_Reverse_Iterator; + + // = Traits + typedef ACE_Message_Queue_Iterator + ITERATOR; + typedef ACE_Message_Queue_Reverse_Iterator + REVERSE_ITERATOR; + + // = Initialization and termination methods. + /** + * Initialize an . The + * determines how many bytes can be stored in a queue before it's + * considered "full." Supplier threads must block until the queue + * is no longer full. The determines how many + * bytes must be in the queue before supplier threads are allowed to + * enqueue additional s. By default, the + * equals the , which means that + * suppliers will be able to enqueue new messages as soon as a + * consumer removes any message from the queue. Making the + * smaller than the forces + * consumers to drain more messages from the queue before suppliers + * can enqueue new messages, which can minimize the "silly window + * syndrome." + */ + ACE_Message_Queue (size_t high_water_mark = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t low_water_mark = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0); + + /** + * Initialize an . The + * determines how many bytes can be stored in a queue before it's + * considered "full." Supplier threads must block until the queue + * is no longer full. The determines how many + * bytes must be in the queue before supplier threads are allowed to + * enqueue additional s. By default, the + * equals the , which means that + * suppliers will be able to enqueue new messages as soon as a + * consumer removes any message from the queue. Making the + * smaller than the forces + * consumers to drain more messages from the queue before suppliers + * can enqueue new messages, which can minimize the "silly window + * syndrome." + */ + virtual int open (size_t hwm = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t lwm = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0); + + /// Close down the message queue and release all resources. + virtual int close (void); + + /// Close down the message queue and release all resources. + virtual ~ACE_Message_Queue (void); + + // = Enqueue and dequeue methods. + + // For the following enqueue and dequeue methods if == 0, + // the caller will block until action is possible, else will wait + // until the absolute time specified in * elapses). These + // calls will return, however, when queue is closed, deactivated, + // when a signal occurs, or if the time specified in timeout + // elapses, (in which case errno = EWOULDBLOCK). + + /** + * Retrieve the first without removing it. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + + /** + * Enqueue an into the in + * accordance with its (0 is lowest priority). FIFO + * order is maintained when messages of the same priority are + * inserted consecutively. Note that uses <{absolute}> + * time rather than <{relative}> time. If the elapses + * without receiving a message -1 is returned and is set to + * . If the queue is deactivated -1 is returned and + * is set to . Otherwise, returns -1 on failure, + * else the number of items still on the queue. + */ + virtual int enqueue_prio (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + /** + * This is an alias for . It's only here for + * backwards compatibility and will go away in a subsequent release. + * Please use instead. Note that uses + * <{absolute}> time rather than <{relative}> time. + */ + virtual int enqueue (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + /** + * Enqueue an at the end of the queue. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + /** + * Enqueue an at the head of the queue. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int enqueue_head (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + /// This method is an alias for the following method. + virtual int dequeue (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + + /** + * Dequeue and return the at the head of the + * queue. Note that uses <{absolute}> time rather than + * <{relative}> time. If the elapses without receiving a + * message -1 is returned and is set to . If + * the queue is deactivated -1 is returned and is set to + * . Otherwise, returns -1 on failure, else the number + * of items still on the queue. + */ + virtual int dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + + // = Check if queue is full/empty. + /// True if queue is full, else false. + virtual int is_full (void); + /// True if queue is empty, else false. + virtual int is_empty (void); + + // = Queue statistic methods. + /** + * Number of total bytes on the queue, i.e., sum of the message + * block sizes. + */ + virtual size_t message_bytes (void); + /** + * Number of total length on the queue, i.e., sum of the message + * block lengths. + */ + virtual size_t message_length (void); + /** + * Number of total messages on the queue. + */ + virtual size_t message_count (void); + + // = Manual changes to these stats (used when queued message blocks + // change size or lengths). + /** + * New value of the number of total bytes on the queue, i.e., sum of + * the message block sizes. + */ + virtual void message_bytes (size_t new_size); + /** + * New value of the number of total length on the queue, i.e., sum + * of the message block lengths. + */ + virtual void message_length (size_t new_length); + + // = Flow control methods. + + /** + * Get high watermark. + */ + virtual size_t high_water_mark (void); + /** + * Set the high watermark, which determines how many bytes can be + * stored in a queue before it's considered "full." + */ + virtual void high_water_mark (size_t hwm); + + /** + * Get low watermark. + */ + virtual size_t low_water_mark (void); + /** + * Set the low watermark, which determines how many bytes must be in + * the queue before supplier threads are allowed to enqueue + * additional s. + */ + virtual void low_water_mark (size_t lwm); + + // = Activation control methods. + + /** + * Deactivate the queue and wakeup all threads waiting on the queue + * so they can continue. No messages are removed from the queue, + * however. Any other operations called until the queue is + * activated again will immediately return -1 with == + * ESHUTDOWN. Returns WAS_INACTIVE if queue was inactive before the + * call and WAS_ACTIVE if queue was active before the call. + */ + virtual int deactivate (void); + + /** + * Reactivate the queue so that threads can enqueue and dequeue + * messages again. Returns WAS_INACTIVE if queue was inactive + * before the call and WAS_ACTIVE if queue was active before the + * call. + */ + virtual int activate (void); + + /// Returns true if is enabled. + virtual int deactivated (void); + + // = Notification hook. + + /** + * This hook is automatically invoked by , + * , and when a new item is inserted + * into the queue. Subclasses can override this method to perform + * specific notification strategies (e.g., signaling events for a + * , notifying a , etc.). In a + * multi-threaded application with concurrent consumers, there is no + * guarantee that the queue will be still be non-empty by the time + * the notification occurs. + */ + virtual int notify (void); + + // = Get/set the notification strategy for the + virtual ACE_Notification_Strategy *notification_strategy (void); + virtual void notification_strategy (ACE_Notification_Strategy *s); + + /// Returns a reference to the lock used by the . + virtual ACE_SYNCH_MUTEX_T &lock (void) + { + // The Sun Forte 6 (CC 5.1) compiler is only happy if this is in the + // header file (j.russell.noseworthy@objectsciences.com) + return this->lock_; + } + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + // = Routines that actually do the enqueueing and dequeueing. + + // These routines assume that locks are held by the corresponding + // public methods. Since they are virtual, you can change the + // queueing mechanism by subclassing from . + + /// Enqueue an in accordance with its priority. + virtual int enqueue_i (ACE_Message_Block *new_item); + + /// Enqueue an at the end of the queue. + virtual int enqueue_tail_i (ACE_Message_Block *new_item); + + /// Enqueue an at the head of the queue. + virtual int enqueue_head_i (ACE_Message_Block *new_item); + + /// Dequeue and return the at the head of the + /// queue. + virtual int dequeue_head_i (ACE_Message_Block *&first_item); + + // = Check the boundary conditions (assumes locks are held). + + /// True if queue is full, else false. + virtual int is_full_i (void); + + /// True if queue is empty, else false. + virtual int is_empty_i (void); + + // = Implementation of the public and methods. + + // These methods assume locks are held. + + /// Deactivate the queue. + virtual int deactivate_i (void); + + /// Activate the queue. + virtual int activate_i (void); + + // = Helper methods to factor out common #ifdef code. + + /// Wait for the queue to become non-full. + virtual int wait_not_full_cond (ACE_Guard &mon, + ACE_Time_Value *timeout); + + /// Wait for the queue to become non-empty. + virtual int wait_not_empty_cond (ACE_Guard &mon, + ACE_Time_Value *timeout); + + /// Inform any threads waiting to enqueue that they can procede. + virtual int signal_enqueue_waiters (void); + + /// Inform any threads waiting to dequeue that they can procede. + virtual int signal_dequeue_waiters (void); + + /// Pointer to head of ACE_Message_Block list. + ACE_Message_Block *head_; + + /// Pointer to tail of ACE_Message_Block list. + ACE_Message_Block *tail_; + + /// Lowest number before unblocking occurs. + size_t low_water_mark_; + + /// Greatest number of bytes before blocking. + size_t high_water_mark_; + + /// Current number of bytes in the queue. + size_t cur_bytes_; + + /// Current length of messages in the queue. + size_t cur_length_; + + /// Current number of messages in the queue. + size_t cur_count_; + + /// Indicates that the queue is inactive. + int deactivated_; + + /// The notification strategy used when a new message is enqueued. + ACE_Notification_Strategy *notification_strategy_; + + // = Synchronization primitives for controlling concurrent access. + /// Protect queue from concurrent access. + ACE_SYNCH_MUTEX_T lock_; + +#if defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) + /// Used to make threads sleep until the queue is no longer empty. + ACE_SYNCH_SEMAPHORE_T not_empty_cond_; + + /// Used to make threads sleep until the queue is no longer full. + ACE_SYNCH_SEMAPHORE_T not_full_cond_; + + /// Number of threads waiting to dequeue a . + size_t dequeue_waiters_; + + /// Number of threads waiting to enqueue a . + size_t enqueue_waiters_; +#else + /// Used to make threads sleep until the queue is no longer empty. + ACE_SYNCH_CONDITION_T not_empty_cond_; + + /// Used to make threads sleep until the queue is no longer full. + ACE_SYNCH_CONDITION_T not_full_cond_; +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + +private: + + // = Disallow these operations. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Message_Queue &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Message_Queue (const ACE_Message_Queue &)) +}; + +// This typedef is used to get around a compiler bug in g++/vxworks. +typedef ACE_Message_Queue ACE_DEFAULT_MESSAGE_QUEUE_TYPE; + + +/** + * @class ACE_Message_Queue_Iterator + * + * @brief Iterator for the . + */ +template +class ACE_Message_Queue_Iterator +{ +public: + // = Initialization method. + ACE_Message_Queue_Iterator (ACE_Message_Queue &queue); + + // = Iteration methods. + /// Pass back the that hasn't been seen in the queue. + /// Returns 0 when all items have been seen, else 1. + int next (ACE_Message_Block *&entry); + + /// Returns 1 when all items have been seen, else 0. + int done (void) const; + + /// Move forward by one element in the queue. Returns 0 when all the + /// items in the set have been seen, else 1. + int advance (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Message_Queue we are iterating over. + ACE_Message_Queue &queue_; + + /// Keeps track of how far we've advanced... + ACE_Message_Block *curr_; +}; + +/** + * @class ACE_Message_Queue_Reverse_Iterator + * + * @brief Reverse Iterator for the . + */ +template +class ACE_Message_Queue_Reverse_Iterator +{ +public: + // = Initialization method. + ACE_Message_Queue_Reverse_Iterator (ACE_Message_Queue &queue); + + // = Iteration methods. + /// Pass back the that hasn't been seen in the queue. + /// Returns 0 when all items have been seen, else 1. + int next (ACE_Message_Block *&entry); + + /// Returns 1 when all items have been seen, else 0. + int done (void) const; + + /// Move forward by one element in the queue. Returns 0 when all the + /// items in the set have been seen, else 1. + int advance (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Message_Queue we are iterating over. + ACE_Message_Queue &queue_; + + /// Keeps track of how far we've advanced... + ACE_Message_Block *curr_; +}; + +/** + * @class ACE_Dynamic_Message_Queue + * + * @brief A derived class which adapts the + * class in order to maintain dynamic priorities for enqueued + * and manage the queue order according + * to these dynamic priorities. + * + * The messages in the queue are managed so as to preserve + * a logical ordering with minimal overhead per enqueue and + * dequeue operation. For this reason, the actual order of + * messages in the linked list of the queue may differ from + * their priority order. As time passes, a message may change + * from pending status to late status, and eventually to beyond + * late status. To minimize reordering overhead under this + * design force, three separate boundaries are maintained + * within the linked list of messages. Messages are dequeued + * preferentially from the head of the pending portion, then + * the head of the late portion, and finally from the head + * of the beyond late portion. In this way, only the boundaries + * need to be maintained (which can be done efficiently, as + * aging messages maintain the same linked list order as they + * progress from one status to the next), with no reordering + * of the messages themselves, while providing correct priority + * ordered dequeueing semantics. + * Head and tail enqueue methods inherited from ACE_Message_Queue + * are made private to prevent out-of-order messages from confusing + * management of the various portions of the queue. Messages in + * the pending portion of the queue whose priority becomes late + * (according to the specific dynamic strategy) advance into + * the late portion of the queue. Messages in the late portion + * of the queue whose priority becomes later than can be represented + * advance to the beyond_late portion of the queue. These behaviors + * support a limited schedule overrun, with pending messages prioritized + * ahead of late messages, and late messages ahead of beyond late + * messages. These behaviors can be modified in derived classes by + * providing alternative definitions for the appropriate virtual methods. + * When filled with messages, the queue's linked list should look like: + * H T + * | | + * B - B - B - B - L - L - L - P - P - P - P - P + * | | | | | | + * BH BT LH LT PH PT + * Where the symbols are as follows: + * H = Head of the entire list + * T = Tail of the entire list + * B = Beyond late message + * BH = Beyond late messages Head + * BT = Beyond late messages Tail + * L = Late message + * LH = Late messages Head + * LT = Late messages Tail + * P = Pending message + * PH = Pending messages Head + * PT = Pending messages Tail + * Caveat: the virtual methods enqueue_tail, enqueue_head, + * and peek_dequeue_head have semantics for the static + * message queues that cannot be guaranteed for dynamic + * message queues. The peek_dequeue_head method just + * calls the base class method, while the two enqueue + * methods call the priority enqueue method. The + * order of messages in the dynamic queue is a function + * of message deadlines and how long they are in the + * queues. You can manipulate these in some cases to + * ensure the correct semantics, but that is not a + * very stable or portable approach (discouraged). + */ +template +class ACE_Dynamic_Message_Queue : public ACE_Message_Queue +{ +public: + // = Initialization and termination methods. + ACE_Dynamic_Message_Queue (ACE_Dynamic_Message_Strategy & message_strategy, + size_t hwm = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t lwm = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0); + + /// Close down the message queue and release all resources. + virtual ~ACE_Dynamic_Message_Queue (void); + + /** + * Detach all messages with status given in the passed flags from + * the queue and return them by setting passed head and tail pointers + * to the linked list they comprise. This method is intended primarily + * as a means of periodically harvesting messages that have missed + * their deadlines, but is available in its most general form. All + * messages are returned in priority order, from head to tail, as of + * the time this method was called. + */ + virtual int remove_messages (ACE_Message_Block *&list_head, + ACE_Message_Block *&list_tail, + u_int status_flags); + + /** + * Dequeue and return the at the head of the + * queue. Returns -1 on failure, else the number of items still on + * the queue. + */ + virtual int dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + + /// Dump the state of the queue. + virtual void dump (void) const; + + /** + * just call priority enqueue method: tail enqueue semantics for dynamic + * message queues are unstable: the message may or may not be where + * it was placed after the queue is refreshed prior to the next + * enqueue or dequeue operation. + */ + virtual int enqueue_tail (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + /** + * just call priority enqueue method: head enqueue semantics for dynamic + * message queues are unstable: the message may or may not be where + * it was placed after the queue is refreshed prior to the next + * enqueue or dequeue operation. + */ + virtual int enqueue_head (ACE_Message_Block *new_item, + ACE_Time_Value *timeout = 0); + + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + + /** + * Enqueue an in accordance with its priority. + * priority may be *dynamic* or *static* or a combination or *both* + * It calls the priority evaluation function passed into the Dynamic + * Message Queue constructor to update the priorities of all + * enqueued messages. + */ + virtual int enqueue_i (ACE_Message_Block *new_item); + + /// enqueue a message in priority order within a given priority status sublist + virtual int sublist_enqueue_i (ACE_Message_Block *new_item, + const ACE_Time_Value ¤t_time, + ACE_Message_Block *&sublist_head, + ACE_Message_Block *&sublist_tail, + ACE_Dynamic_Message_Strategy::Priority_Status status); + + /** + * Dequeue and return the at the head of the + * logical queue. Attempts first to dequeue from the pending + * portion of the queue, or if that is empty from the late portion, + * or if that is empty from the beyond late portion, or if that is + * empty just sets the passed pointer to zero and returns -1. + */ + virtual int dequeue_head_i (ACE_Message_Block *&first_item); + + /// Refresh the queue using the strategy + /// specific priority status function. + virtual int refresh_queue (const ACE_Time_Value & current_time); + + /// Refresh the pending queue using the strategy + /// specific priority status function. + virtual int refresh_pending_queue (const ACE_Time_Value & current_time); + + /// Refresh the late queue using the strategy + /// specific priority status function. + virtual int refresh_late_queue (const ACE_Time_Value & current_time); + + /// Pointer to head of the pending messages + ACE_Message_Block *pending_head_; + + /// Pointer to tail of the pending messages + ACE_Message_Block *pending_tail_; + + /// Pointer to head of the late messages + ACE_Message_Block *late_head_; + + /// Pointer to tail of the late messages + ACE_Message_Block *late_tail_; + + /// Pointer to head of the beyond late messages + ACE_Message_Block *beyond_late_head_; + + /// Pointer to tail of the beyond late messages + ACE_Message_Block *beyond_late_tail_; + + /// Pointer to a dynamic priority evaluation function. + ACE_Dynamic_Message_Strategy &message_strategy_; + +private: + // = Disallow public access to these operations. + + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Dynamic_Message_Queue &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Dynamic_Message_Queue (const ACE_Dynamic_Message_Queue &)) + + // provide definitions for these (just call base class method), + // but make them private so they're not accessible outside the class + + /// private method to hide public base class method: just calls base class method + virtual int peek_dequeue_head (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout = 0); + +}; + +/** + * @class ACE_Message_Queue_Factory + * + * @brief ACE_Message_Queue_Factory is a static factory class template which + * provides a separate factory method for each of the major kinds of + * priority based message dispatching: static, earliest deadline first + * (EDF), and minimum laxity first (MLF). + * + * The ACE_Dynamic_Message_Queue class assumes responsibility for + * releasing the resources of the strategy with which it was + * constructed: the user of a message queue constructed by + * any of these factory methods is only responsible for + * ensuring destruction of the message queue itself. + */ +template +class ACE_Message_Queue_Factory +{ +public: + /// factory method for a statically prioritized ACE_Message_Queue + static ACE_Message_Queue * + create_static_message_queue (size_t hwm = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t lwm = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0); + + /// factory method for a dynamically prioritized (by time to deadline) ACE_Dynamic_Message_Queue + static ACE_Dynamic_Message_Queue * + create_deadline_message_queue (size_t hwm = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t lwm = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0, + u_long static_bit_field_mask = 0x3FFUL, // 2^(10) - 1 + u_long static_bit_field_shift = 10, // 10 low order bits + u_long dynamic_priority_max = 0x3FFFFFUL, // 2^(22)-1 + u_long dynamic_priority_offset = 0x200000UL); // 2^(22-1) + + /// factory method for a dynamically prioritized (by laxity) ACE_Dynamic_Message_Queue + static ACE_Dynamic_Message_Queue * + create_laxity_message_queue (size_t hwm = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t lwm = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0, + u_long static_bit_field_mask = 0x3FFUL, // 2^(10) - 1 + u_long static_bit_field_shift = 10, // 10 low order bits + u_long dynamic_priority_max = 0x3FFFFFUL, // 2^(22)-1 + u_long dynamic_priority_offset = 0x200000UL); // 2^(22-1) + + +#if defined (VXWORKS) + + /// factory method for a wrapped VxWorks message queue + static ACE_Message_Queue_Vx * + create_Vx_message_queue (size_t max_messages, size_t max_message_length, + ACE_Notification_Strategy *ns = 0); + +#endif /* defined (VXWORKS) */ + +#if defined (ACE_WIN32) && (ACE_HAS_WINNT4 != 0) + + /// factory method for a NT message queue. + static ACE_Message_Queue_NT * + create_NT_message_queue (size_t max_threads); + +#endif /* ACE_WIN32 && ACE_HAS_WINNT4 != 0 */ +}; + +/** + * @class ACE_Message_Queue_Ex + * + * @brief A threaded message queueing facility, modeled after the + * queueing facilities in System V STREAMs. + * + * An is a strongly-typed version of the + * . If + * is then all operations are + * thread-safe. Otherwise, if it's then there's no + * locking overhead. + */ +template +class ACE_Message_Queue_Ex +{ +public: + + // = Default priority value. + enum + { + DEFAULT_PRIORITY = 0 + }; + +#if 0 + // @@ Iterators are not implemented yet... + + friend class ACE_Message_Queue_Iterator; + friend class ACE_Message_Queue_Reverse_Iterator; + + // = Traits + typedef ACE_Message_Queue_Iterator + ITERATOR; + typedef ACE_Message_Queue_Reverse_Iterator + REVERSE_ITERATOR; +#endif /* 0 */ + + // = Initialization and termination methods. + + /** + * Initialize an . The + * determines how many bytes can be stored in a queue before it's + * considered "full." Supplier threads must block until the queue + * is no longer full. The determines how many + * bytes must be in the queue before supplier threads are allowed to + * enqueue additional s. By default, the + * equals the , which means that + * suppliers will be able to enqueue new messages as soon as a + * consumer removes any message from the queue. Making the + * smaller than the forces + * consumers to drain more messages from the queue before suppliers + * can enqueue new messages, which can minimize the "silly window + * syndrome." + */ + ACE_Message_Queue_Ex (size_t high_water_mark = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t low_water_mark = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0); + + /** + * Initialize an . The + * determines how many bytes can be stored in a queue before it's + * considered "full." Supplier threads must block until the queue + * is no longer full. The determines how many + * bytes must be in the queue before supplier threads are allowed to + * enqueue additional s. By default, the + * equals the , which means that + * suppliers will be able to enqueue new messages as soon as a + * consumer removes any message from the queue. Making the + * smaller than the forces + * consumers to drain more messages from the queue before suppliers + * can enqueue new messages, which can minimize the "silly window + * syndrome." + */ + virtual int open (size_t hwm = ACE_Message_Queue_Base::DEFAULT_HWM, + size_t lwm = ACE_Message_Queue_Base::DEFAULT_LWM, + ACE_Notification_Strategy * = 0); + + /// Close down the message queue and release all resources. + virtual int close (void); + + /// Close down the message queue and release all resources. + virtual ~ACE_Message_Queue_Ex (void); + + // = Enqueue and dequeue methods. + + // For the following enqueue and dequeue methods if == 0, + // the caller will block until action is possible, else will wait + // until the absolute time specified in * elapses). These + // calls will return, however, when queue is closed, deactivated, + // when a signal occurs, or if the time specified in timeout + // elapses, (in which case errno = EWOULDBLOCK). + + /** + * Retrieve the first without removing it. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int peek_dequeue_head (ACE_MESSAGE_TYPE *&first_item, + ACE_Time_Value *timeout = 0); + + /** + * Enqueue an into the in + * accordance with its (0 is lowest priority). FIFO + * order is maintained when messages of the same priority are + * inserted consecutively. Note that uses <{absolute}> + * time rather than <{relative}> time. If the elapses + * without receiving a message -1 is returned and is set to + * . If the queue is deactivated -1 is returned and + * is set to . Otherwise, returns -1 on failure, + * else the number of items still on the queue. + */ + virtual int enqueue_prio (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout = 0); + + /** + * This is an alias for . It's only here for + * backwards compatibility and will go away in a subsequent release. + * Please use instead. Note that uses + * <{absolute}> time rather than <{relative}> time. + */ + virtual int enqueue (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout = 0); + + /** + * Enqueue an at the end of the queue. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int enqueue_tail (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout = 0); + + /** + * Enqueue an at the head of the queue. Note + * that uses <{absolute}> time rather than <{relative}> + * time. If the elapses without receiving a message -1 is + * returned and is set to . If the queue is + * deactivated -1 is returned and is set to . + * Otherwise, returns -1 on failure, else the number of items still + * on the queue. + */ + virtual int enqueue_head (ACE_MESSAGE_TYPE *new_item, + ACE_Time_Value *timeout = 0); + + /// This method is an alias for the following method. + virtual int dequeue (ACE_MESSAGE_TYPE *&first_item, + ACE_Time_Value *timeout = 0); + // This method is an alias for the following method. + + /** + * Dequeue and return the at the head of the + * queue. Note that uses <{absolute}> time rather than + * <{relative}> time. If the elapses without receiving a + * message -1 is returned and is set to . If + * the queue is deactivated -1 is returned and is set to + * . Otherwise, returns -1 on failure, else the number + * of items still on the queue. + */ + virtual int dequeue_head (ACE_MESSAGE_TYPE *&first_item, + ACE_Time_Value *timeout = 0); + + // = Check if queue is full/empty. + /// True if queue is full, else false. + virtual int is_full (void); + /// True if queue is empty, else false. + virtual int is_empty (void); + + + // = Queue statistic methods. + /** + * Number of total bytes on the queue, i.e., sum of the message + * block sizes. + */ + virtual size_t message_bytes (void); + /** + * Number of total length on the queue, i.e., sum of the message + * block lengths. + */ + virtual size_t message_length (void); + /** + * Number of total messages on the queue. + */ + virtual size_t message_count (void); + + // = Manual changes to these stats (used when queued message blocks + // change size or lengths). + /** + * New value of the number of total bytes on the queue, i.e., sum of + * the message block sizes. + */ + virtual void message_bytes (size_t new_size); + /** + * New value of the number of total length on the queue, i.e., sum + * of the message block lengths. + */ + virtual void message_length (size_t new_length); + + // = Flow control methods. + /** + * Get high watermark. + */ + virtual size_t high_water_mark (void); + /** + * Set the high watermark, which determines how many bytes can be + * stored in a queue before it's considered "full." + */ + virtual void high_water_mark (size_t hwm); + + /** + * Get low watermark. + */ + virtual size_t low_water_mark (void); + /** + * Set the low watermark, which determines how many bytes must be in + * the queue before supplier threads are allowed to enqueue + * additional s. + */ + virtual void low_water_mark (size_t lwm); + + // = Activation control methods. + + /** + * Deactivate the queue and wakeup all threads waiting on the queue + * so they can continue. No messages are removed from the queue, + * however. Any other operations called until the queue is + * activated again will immediately return -1 with == + * ESHUTDOWN. Returns WAS_INACTIVE if queue was inactive before the + * call and WAS_ACTIVE if queue was active before the call. + */ + virtual int deactivate (void); + + /** + * Reactivate the queue so that threads can enqueue and dequeue + * messages again. Returns WAS_INACTIVE if queue was inactive + * before the call and WAS_ACTIVE if queue was active before the + * call. + */ + virtual int activate (void); + + /// Returns true if is enabled. + virtual int deactivated (void); + + // = Notification hook. + + /** + * This hook is automatically invoked by , + * , and when a new item is inserted + * into the queue. Subclasses can override this method to perform + * specific notification strategies (e.g., signaling events for a + * , notifying a , etc.). In a + * multi-threaded application with concurrent consumers, there is no + * guarantee that the queue will be still be non-empty by the time + * the notification occurs. + */ + virtual int notify (void); + + /// Get/set the notification strategy for the + virtual ACE_Notification_Strategy *notification_strategy (void); + virtual void notification_strategy (ACE_Notification_Strategy *s); + + /// Returns a reference to the lock used by the . + virtual ACE_SYNCH_MUTEX_T &lock (void) + { + // The Sun Forte 6 (CC 5.1) compiler is only happy if this is in the + // header file (j.russell.noseworthy@objectsciences.com) + return this->queue_.lock (); + } + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Implement this via an . + ACE_Message_Queue queue_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Message_Queue_T.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Message_Queue_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Message_Queue_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_MESSAGE_QUEUE_T_H */ diff --git a/ace/Streams/Message_Queue_T.i b/ace/Streams/Message_Queue_T.i new file mode 100644 index 00000000000..297cd12830e --- /dev/null +++ b/ace/Streams/Message_Queue_T.i @@ -0,0 +1,303 @@ +/* -*- C++ -*- */ +// $Id$ + +template ACE_INLINE int +ACE_Message_Queue::dequeue (ACE_Message_Block *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue::dequeue"); + return this->dequeue_head (first_item, timeout); +} + +template ACE_INLINE ACE_Notification_Strategy * +ACE_Message_Queue::notification_strategy (void) +{ + ACE_TRACE ("ACE_Message_Queue::notification_strategy"); + + return this->notification_strategy_; +} + +template ACE_INLINE void +ACE_Message_Queue::notification_strategy (ACE_Notification_Strategy *s) +{ + ACE_TRACE ("ACE_Message_Queue::notification_strategy"); + + this->notification_strategy_ = s; +} + +// Check if queue is empty (does not hold locks). + +template ACE_INLINE int +ACE_Message_Queue::is_empty_i (void) +{ + ACE_TRACE ("ACE_Message_Queue::is_empty_i"); + return this->tail_ == 0; +} + +// Check if queue is full (does not hold locks). + +template ACE_INLINE int +ACE_Message_Queue::is_full_i (void) +{ + ACE_TRACE ("ACE_Message_Queue::is_full_i"); + return this->cur_bytes_ > this->high_water_mark_; +} + +// Check if queue is empty (holds locks). + +template ACE_INLINE int +ACE_Message_Queue::is_empty (void) +{ + ACE_TRACE ("ACE_Message_Queue::is_empty"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + return this->is_empty_i (); +} + +// Check if queue is full (holds locks). + +template ACE_INLINE int +ACE_Message_Queue::is_full (void) +{ + ACE_TRACE ("ACE_Message_Queue::is_full"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + return this->is_full_i (); +} + +template ACE_INLINE size_t +ACE_Message_Queue::high_water_mark (void) +{ + ACE_TRACE ("ACE_Message_Queue::high_water_mark"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, 0); + + return this->high_water_mark_; +} + +template ACE_INLINE void +ACE_Message_Queue::high_water_mark (size_t hwm) +{ + ACE_TRACE ("ACE_Message_Queue::high_water_mark"); + ACE_GUARD (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_); + + this->high_water_mark_ = hwm; +} + +template ACE_INLINE size_t +ACE_Message_Queue::low_water_mark (void) +{ + ACE_TRACE ("ACE_Message_Queue::low_water_mark"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, 0); + + return this->low_water_mark_; +} + +template ACE_INLINE void +ACE_Message_Queue::low_water_mark (size_t lwm) +{ + ACE_TRACE ("ACE_Message_Queue::low_water_mark"); + ACE_GUARD (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_); + + this->low_water_mark_ = lwm; +} + +template ACE_INLINE size_t +ACE_Message_Queue::message_bytes (void) +{ + ACE_TRACE ("ACE_Message_Queue::message_bytes"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, 0); + + return this->cur_bytes_; +} + +template ACE_INLINE size_t +ACE_Message_Queue::message_length (void) +{ + ACE_TRACE ("ACE_Message_Queue::message_length"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, 0); + + return this->cur_length_; +} + +template ACE_INLINE size_t +ACE_Message_Queue::message_count (void) +{ + ACE_TRACE ("ACE_Message_Queue::message_count"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, 0); + + return this->cur_count_; +} + +template ACE_INLINE int +ACE_Message_Queue::activate (void) +{ + ACE_TRACE ("ACE_Message_Queue::activate"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + return this->activate_i (); +} + +template ACE_INLINE int +ACE_Message_Queue::deactivate (void) +{ + ACE_TRACE ("ACE_Message_Queue::deactivate"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + return this->deactivate_i (); +} + +template ACE_INLINE int +ACE_Message_Queue::deactivated (void) +{ + ACE_TRACE ("ACE_Message_Queue::deactivated"); + + return this->deactivated_; +} + +#if 0 +// The Sun Forte 6 (CC 5.1) compiler is only happy if this is in the +// header file (j.russell.noseworthy@objectsciences.com) + +template ACE_INLINE ACE_SYNCH_MUTEX_T & +ACE_Message_Queue::lock (void) +{ + return this->lock_; +} +#endif /* 0 */ + +ACE_ALLOC_HOOK_DEFINE(ACE_Message_Queue_Reverse_Iterator) + +template ACE_INLINE int +ACE_Message_Queue_Ex::dequeue (ACE_MESSAGE_TYPE *&first_item, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::dequeue"); + + return this->dequeue_head (first_item, timeout); +} + +template ACE_INLINE ACE_Notification_Strategy * +ACE_Message_Queue_Ex::notification_strategy (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::notification_strategy"); + + return this->queue_.notification_strategy (); +} + +template ACE_INLINE void +ACE_Message_Queue_Ex::notification_strategy (ACE_Notification_Strategy *s) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::notification_strategy"); + + this->queue_.notification_strategy (s); +} + +// Check if queue is empty (holds locks). + +template ACE_INLINE int +ACE_Message_Queue_Ex::is_empty (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::is_empty"); + + return this->queue_.is_empty (); +} + +// Check if queue is full (holds locks). + +template ACE_INLINE int +ACE_Message_Queue_Ex::is_full (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::is_full"); + + return this->queue_.is_full (); +} + +template ACE_INLINE size_t +ACE_Message_Queue_Ex::high_water_mark (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::high_water_mark"); + + return this->queue_.high_water_mark (); +} + +template ACE_INLINE void +ACE_Message_Queue_Ex::high_water_mark (size_t hwm) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::high_water_mark"); + + this->queue_.high_water_mark (hwm); +} + +template ACE_INLINE size_t +ACE_Message_Queue_Ex::low_water_mark (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::low_water_mark"); + + return this->queue_.low_water_mark (); +} + +template ACE_INLINE void +ACE_Message_Queue_Ex::low_water_mark (size_t lwm) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::low_water_mark"); + + this->queue_.low_water_mark (lwm); +} + +template ACE_INLINE size_t +ACE_Message_Queue_Ex::message_bytes (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::message_bytes"); + + return this->queue_.message_bytes (); +} + +template ACE_INLINE size_t +ACE_Message_Queue_Ex::message_length (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::message_length"); + + return this->queue_.message_length (); +} + +template ACE_INLINE size_t +ACE_Message_Queue_Ex::message_count (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::message_count"); + + return this->queue_.message_count (); +} + +template ACE_INLINE int +ACE_Message_Queue_Ex::activate (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::activate"); + + return this->queue_.activate (); +} + +template ACE_INLINE int +ACE_Message_Queue_Ex::deactivate (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::deactivate"); + + return this->queue_.deactivate (); +} + +template ACE_INLINE int +ACE_Message_Queue_Ex::deactivated (void) +{ + ACE_TRACE ("ACE_Message_Queue_Ex::deactivated"); + + return this->queue_.deactivated (); +} + +#if 0 +// The Sun Forte 6 (CC 5.1) compiler is only happy if this is in the +// header file (j.russell.noseworthy@objectsciences.com) +template ACE_INLINE ACE_SYNCH_MUTEX_T & +ACE_Message_Queue_Ex::lock (void) +{ + return this->queue_.lock (); +} +#endif /* 0 */ diff --git a/ace/Streams/Module.cpp b/ace/Streams/Module.cpp new file mode 100644 index 00000000000..e21b2cc49a6 --- /dev/null +++ b/ace/Streams/Module.cpp @@ -0,0 +1,266 @@ +// Module.cpp +// $Id$ + +#ifndef ACE_MODULE_C +#define ACE_MODULE_C + +#include "ace/Module.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Stream_Modules.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Module.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Module, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Module) + +template void +ACE_Module::dump (void) const +{ + ACE_TRACE ("ACE_Module::dump"); +} + +template void +ACE_Module::writer (ACE_Task *q, + int flags /* = M_DELETE_WRITER */) +{ + ACE_TRACE ("ACE_Module::writer"); + + // Close and maybe delete old writer + this->close_i (1, flags); + + this->q_pair_[1] = q; + + if (q != 0) + { + ACE_CLR_BITS (q->flags_, ACE_Task_Flags::ACE_READER); + // Set the q's module pointer to point to us. + q->mod_ = this; + } + + // Don't allow the caller to change the reader status. + ACE_SET_BITS (flags_, (flags & M_DELETE_WRITER)); +} + +template void +ACE_Module::reader (ACE_Task *q, + int flags /* = M_DELETE_READER */) +{ + ACE_TRACE ("ACE_Module::reader"); + + // Close and maybe delete old writer + this->close_i (0, flags); + + this->q_pair_[0] = q; + + if (q != 0) + { + ACE_SET_BITS (q->flags_, ACE_Task_Flags::ACE_READER); + // Set the q's module pointer to point to us. + q->mod_ = this; + } + + // don't allow the caller to change the reader status + ACE_SET_BITS (flags_, (flags & M_DELETE_READER)); +} + +// Link this ACE_Module on top of ACE_Module M. + +template void +ACE_Module::link (ACE_Module *m) +{ + ACE_TRACE ("ACE_Module::link"); + this->next (m); + this->writer ()->next (m->writer ()); + m->reader ()->next (this->reader ()); +} + +template int +ACE_Module::open (const ACE_TCHAR *mod_name, + ACE_Task *writer_q, + ACE_Task *reader_q, + void *arg, + int flags /* = M_DELETE */) +{ + ACE_TRACE ("ACE_Module::open"); + this->name (mod_name); + this->arg_ = arg; + + // We may already have readers and/or writers. + if (this->reader ()) + this->close_i (0, M_DELETE_READER); + + if (this->writer ()) + this->close_i (1, M_DELETE_WRITER); + + if (writer_q == 0) + { + ACE_NEW_RETURN (writer_q, + ACE_Thru_Task, + -1); + ACE_SET_BITS (flags, M_DELETE_WRITER); + } + + if (reader_q == 0) + { + ACE_NEW_RETURN (reader_q, + ACE_Thru_Task, + -1); + ACE_SET_BITS (flags, M_DELETE_READER); + } + + this->reader (reader_q); + this->writer (writer_q); + + // Save the flags + this->flags_ = flags; + + // Make sure that the memory is allocated before proceding. + if (writer_q == 0 || reader_q == 0) + { + // These calls will delete writer_q and/or reader_q, if + // necessary. + this->close_i (0, M_DELETE_READER); + this->close_i (1, M_DELETE_WRITER); + + errno = ENOMEM; + return -1; + } + + // Setup back pointers (this must come last, after we've made sure + // there's memory allocated here. + reader_q->mod_ = this; + writer_q->mod_ = this; + + return 0; +} + +// Set and get pointer to sibling ACE_Task in ACE_Module. + +template ACE_Task * +ACE_Module::sibling (ACE_Task *orig) +{ + ACE_TRACE ("ACE_Module::sibling"); + if (this->q_pair_[0] == orig) + return this->q_pair_[1]; + else if (this->q_pair_[1] == orig) + return this->q_pair_[0]; + else + return 0; +} + +template +ACE_Module::ACE_Module (void) + : flags_ (0) +{ + ACE_TRACE ("ACE_Module::ACE_Module"); + this->name (ACE_LIB_TEXT ("")); + // Do nothing... + this->q_pair_[0] = 0; + this->q_pair_[1] = 0; +} + +template +ACE_Module::~ACE_Module (void) +{ + ACE_TRACE ("ACE_Module::~ACE_Module"); + + // Only close down if we haven't already done so. + if (this->reader () || this->writer ()) + this->close (); +} + +template +ACE_Module::ACE_Module (const ACE_TCHAR *mod_name, + ACE_Task *writer_q, + ACE_Task *reader_q, + void *args, + int flags /* = M_DELETE */) + : flags_ (0) +{ + ACE_TRACE ("ACE_Module::ACE_Module"); + + this->q_pair_[0] = 0; + this->q_pair_[1] = 0; + + if (this->open (mod_name, writer_q, reader_q, args, flags) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Module"))); +} + +template int +ACE_Module::close (int flags /* = M_DELETE_NONE */) +{ + ACE_TRACE ("ACE_Module::close"); + + int result = 0; + + ACE_SET_BITS (flags_, flags); + + if (this->close_i (0, flags) == -1) + result = -1; + + if (this->close_i (1, flags) == -1) + result = -1; + + return result; +} + +template int +ACE_Module::close_i (int which, + int flags) +{ + ACE_TRACE ("ACE_Module::close_i"); + + if (this->q_pair_[which] == 0) + return 0; + + // Copy task pointer to prevent problems when ACE_Task::close + // changes the task pointer + ACE_Task *task = this->q_pair_[which]; + + // Change so that close doesn't get called again from the task base. + + // Now close the task. + int result = 0; + + if (task->module_closed () == -1) + result = -1; + + task->flush (); + task->next (0); + + // Should we also delete it ? + if (flags != M_DELETE_NONE + && ACE_BIT_ENABLED (flags_, which + 1)) + { + // Only delete the Tasks if there aren't any more threads + // running in them. + task->wait (); + + // If this assert happens it is likely because the task was + // activated with the THR_DETACHED flag, which means that we + // can't join() with the thread. Not using THR_DETACHED should + // solve this problem. + ACE_ASSERT (task->thr_count () == 0); + + delete task; + } + + // Set the tasks pointer to 0 so that we don't try to close() + // this object again if the destructor gets called. + this->q_pair_[which] = 0; + + // Finally remove the delete bit. + ACE_CLR_BITS (flags_, which + 1); + + return result; +} +#endif /* ACE_MODULE_C */ diff --git a/ace/Streams/Module.h b/ace/Streams/Module.h new file mode 100644 index 00000000000..735220a55ba --- /dev/null +++ b/ace/Streams/Module.h @@ -0,0 +1,207 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Module.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_MODULE_H +#define ACE_MODULE_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Task_T.h" + +/** + * @class ACE_Module_Base + * + * @brief Workaround HP/C++ compiler bug with enums in templates. + * + * Certain C++ compilers, e.g., the HP/UX 10.x and 9.x compilers, + * seem to fail if enums are defined inside a template, hence we + * have to move them into a base class. + */ +class ACE_Export ACE_Module_Base +{ +public: + enum + { + /// Indicates that should not delete any Tasks. + M_DELETE_NONE = 0, + + /// Indicates that should delete the writer Task. + M_DELETE_READER = 1, + + /// Indicates that should delete the reader Task. + M_DELETE_WRITER = 2, + + /// Indicates that deletes the Tasks. + /** + * Don't change this value without updating the same enum in class + * ACE_Stream... + * The and flags may be or'ed + * together. + */ + M_DELETE = 3 + }; +}; + +/** + * @class ACE_Module + * + * @brief An abstraction for managing a bi-directional flow of messages. + * + * This is based on the Module concept in System V Streams, + * which contains a pair of Tasks, one for handling upstream + * processing, one for handling downstream processing. In + * general, you shouldn't subclass from this class, but instead + * subclass from the . + */ +template +class ACE_Module : public ACE_Module_Base +{ +public: + friend class ACE_Shutup_GPlusPlus; // Turn off g++ warning + + // = Initialization and termination methods. + /// Create an empty Module. + ACE_Module (void); + + /// Shutdown the Module. + ~ACE_Module (void); + + /// Create an initialized module with as its identity + /// and and as its tasks. + ACE_Module (const ACE_TCHAR *module_name, + ACE_Task *writer = 0, + ACE_Task *reader = 0, + void *args = 0, + int flags = M_DELETE); + + /** + * Create an initialized module with as its identity + * and and as its tasks. Previously register + * reader or writers or closed down and deleted according to the + * value of flags_. Should not be called from within + * . + */ + int open (const ACE_TCHAR *module_name, + ACE_Task *writer = 0, + ACE_Task *reader = 0, + void *a = 0, + int flags = M_DELETE); + + /** + * Close down the Module and its Tasks. The flags argument can be + * used to override the default behaviour, which depends on previous + * values in calls to c'tor, , , and . + * A previous value M_DELETE[_XXX] can not be overridden. Should + * not be called from within . + */ + int close (int flags = M_DELETE_NONE); + + // = ACE_Task manipulation routines + /// Get the writer task. + ACE_Task *writer (void); + + /** + * Set the writer task. can be used to indicate that the + * module should delete the writer during a call to close or to the + * destructor. If a previous writer exists, it is closed. It may + * also be deleted, depending on the old flags_ value. Should not + * be called from within . + */ + void writer (ACE_Task *q, int flags = M_DELETE_WRITER); + + /// Get the reader task. + ACE_Task *reader (void); + + /** + * Set the reader task. can be used to indicate that the + * module should delete the reader during a call to close or to the + * destructor. If a previous reader exists, it is closed. It may + * also be deleted, depending on the old flags_ value. Should not + * be called from within . + */ + void reader (ACE_Task *q, int flags = M_DELETE_READER); + + /// Set and get pointer to sibling in an + ACE_Task *sibling (ACE_Task *orig); + + // = Identify the module + /// Get the module name. + /// Set the module name. + const ACE_TCHAR *name (void) const; + void name (const ACE_TCHAR *); + + // = Argument to the Tasks. + /// Get the argument passed to the tasks. + void *arg (void) const; + + /// Set the argument passed to the tasks. + void arg (void *); + + /// Link to other modules in the ustream stack + void link (ACE_Module *m); + + /// Get the next pointer to the module above in the stream. + ACE_Module *next (void); + + /// Set the next pointer to the module above in the stream. + void next (ACE_Module *m); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Implements the close operation for either the reader or the + /// writer task (depending on ). + int close_i (int which, int flags); + + /// Pair of Tasks that form the "read-side" and "write-side" of the + /// ACE_Module partitioning. + ACE_Task *q_pair_[2]; + + /// Name of the ACE_Module. + ACE_TCHAR name_[MAXNAMLEN + 1]; + + /// Next ACE_Module in the stack. + ACE_Module *next_; + + /// Argument passed through to the reader and writer task when they + /// are opened. + void *arg_; + + /// Holds flags which are used to determine if the reader and writer + /// task have to be deleted on exit + int flags_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Module.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Module.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Module.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_MODULE_H */ diff --git a/ace/Streams/Module.i b/ace/Streams/Module.i new file mode 100644 index 00000000000..f0ecad5881c --- /dev/null +++ b/ace/Streams/Module.i @@ -0,0 +1,62 @@ +/* -*- C++ -*- */ +// $Id$ + +// Module.i + +template ACE_INLINE void * +ACE_Module::arg (void) const +{ + ACE_TRACE ("ACE_Module::arg"); + return this->arg_; +} + +template ACE_INLINE void +ACE_Module::arg (void *a) +{ + ACE_TRACE ("ACE_Module::arg"); + this->arg_ = a; +} + +template ACE_INLINE const ACE_TCHAR * +ACE_Module::name (void) const +{ + ACE_TRACE ("ACE_Module::name"); + return this->name_; +} + +template ACE_INLINE void +ACE_Module::name (const ACE_TCHAR *n) +{ + ACE_TRACE ("ACE_Module::name"); + ACE_OS::strsncpy (this->name_, n, MAXNAMLEN); +} + +template ACE_INLINE ACE_Task * +ACE_Module::writer (void) +{ + ACE_TRACE ("ACE_Module::writer"); + return this->q_pair_[1]; +} + +template ACE_INLINE ACE_Task * +ACE_Module::reader (void) +{ + ACE_TRACE ("ACE_Module::reader"); + return this->q_pair_[0]; +} + +template ACE_INLINE ACE_Module * +ACE_Module::next (void) +{ + ACE_TRACE ("ACE_Module::next"); + return this->next_; +} + +template ACE_INLINE void +ACE_Module::next (ACE_Module *m) +{ + ACE_TRACE ("ACE_Module::next"); + this->next_ = m; +} + + diff --git a/ace/Streams/Notification_Strategy.cpp b/ace/Streams/Notification_Strategy.cpp new file mode 100644 index 00000000000..77d5dc235b0 --- /dev/null +++ b/ace/Streams/Notification_Strategy.cpp @@ -0,0 +1,18 @@ +#include "ace/Notification_Strategy.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Notification_Strategy.inl" +#endif /* __ACE_INLINE __ */ + +ACE_RCSID(ace, Strategies, "$Id$") + +ACE_Notification_Strategy::ACE_Notification_Strategy (ACE_Event_Handler *eh, + ACE_Reactor_Mask mask) + : eh_ (eh), + mask_ (mask) +{ +} + +ACE_Notification_Strategy::~ACE_Notification_Strategy (void) +{ +} diff --git a/ace/Streams/Notification_Strategy.h b/ace/Streams/Notification_Strategy.h new file mode 100644 index 00000000000..e1ec8b3f034 --- /dev/null +++ b/ace/Streams/Notification_Strategy.h @@ -0,0 +1,63 @@ +/* -*- C++ -*- */ +//============================================================================= +/** + * @file Notification_Strategy.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= +#ifndef ACE_NOTIFICATION_STRATEGY_H +#define ACE_NOTIFICATION_STRATEGY_H +#include "ace/pre.h" + +#include "ace/Event_Handler.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward decls. +class ACE_Reactor; + +/** + * @class ACE_Notification_Strategy + * + * @brief Abstract class used for notifying an interested party + * + * A vehicle for extending the behavior of ACE_Message_Queue wrt + * notification *without subclassing*. Thus, it's an example of + * the Bridge/Strategy patterns. + */ +class ACE_Export ACE_Notification_Strategy +{ +public: + ACE_Notification_Strategy (ACE_Event_Handler *eh, + ACE_Reactor_Mask mask); + virtual ~ACE_Notification_Strategy (void); + + virtual int notify (void) = 0; + virtual int notify (ACE_Event_Handler *, + ACE_Reactor_Mask mask) = 0; + + // Get/Set the event handler + ACE_Event_Handler *event_handler (void); + void event_handler (ACE_Event_Handler *eh); + + // Get/Set the reactor mask + ACE_Reactor_Mask mask (void); + void mask (ACE_Reactor_Mask m); + +protected: + ACE_Event_Handler *eh_; + ACE_Reactor_Mask mask_; +}; + + +#if defined (__ACE_INLINE__) +#include "ace/Notification_Strategy.inl" +#endif /* __ACE_INLINE __ */ + +#include "ace/post.h" +#endif /*ACE_NOTIFICATION_STRATEGY_H */ diff --git a/ace/Streams/Notification_Strategy.inl b/ace/Streams/Notification_Strategy.inl new file mode 100644 index 00000000000..aa89acddcbb --- /dev/null +++ b/ace/Streams/Notification_Strategy.inl @@ -0,0 +1,26 @@ +/* -*- C++ -*- */ +//$Id$ + +ACE_INLINE ACE_Event_Handler * +ACE_Notification_Strategy::event_handler (void) +{ + return eh_; +} + +ACE_INLINE void +ACE_Notification_Strategy::event_handler (ACE_Event_Handler *eh) +{ + this->eh_ = eh; +} + +ACE_INLINE ACE_Reactor_Mask +ACE_Notification_Strategy::mask (void) +{ + return mask_; +} + +ACE_INLINE void +ACE_Notification_Strategy::mask (ACE_Reactor_Mask m) +{ + this->mask_ = m; +} diff --git a/ace/Streams/Read_Buffer.cpp b/ace/Streams/Read_Buffer.cpp new file mode 100644 index 00000000000..2b88461446e --- /dev/null +++ b/ace/Streams/Read_Buffer.cpp @@ -0,0 +1,162 @@ +// $Id$ + +#include "ace/Read_Buffer.h" +#include "ace/Service_Config.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Read_Buffer.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Read_Buffer, "$Id$") + +void +ACE_Read_Buffer::dump (void) const +{ + ACE_TRACE ("ACE_Read_Buffer::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("size_ = %d"), this->size_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\noccurrences_ = %d"), this->occurrences_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nstream_ = %x"), this->stream_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nallocator_ = %x"), this->allocator_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Read_Buffer::ACE_Read_Buffer (FILE *fp, + int close_on_delete, + ACE_Allocator *alloc) + : stream_ (fp), + close_on_delete_ (close_on_delete), + allocator_ (alloc) +{ + ACE_TRACE ("ACE_Read_Buffer::ACE_Read_Buffer"); + if (this->allocator_ == 0) + this->allocator_ = ACE_Allocator::instance (); +} + +ACE_Read_Buffer::ACE_Read_Buffer (ACE_HANDLE handle, + int close_on_delete, + ACE_Allocator *alloc) + : stream_ (ACE_OS::fdopen (handle, ACE_LIB_TEXT ("r"))), + close_on_delete_ (close_on_delete), + allocator_ (alloc) +{ + ACE_TRACE ("ACE_Read_Buffer::ACE_Read_Buffer"); + + if (this->allocator_ == 0) + this->allocator_ = ACE_Allocator::instance (); +} + +ACE_Read_Buffer::~ACE_Read_Buffer (void) +{ + ACE_TRACE ("ACE_Read_Buffer::~ACE_Read_Buffer"); + + if (this->close_on_delete_) + ACE_OS::fclose (this->stream_); +} + +// Input: term the character to terminate on +// search the character to search for +// replace the character with which to replace search +// Output: a buffer containing the contents of stream +// Method: call the recursive helper function read_helper + +char * +ACE_Read_Buffer::read (int term, int search, int replace) +{ + ACE_TRACE ("ACE_Read_Buffer::read"); + this->occurrences_ = 0; + this->size_ = 0; + return this->rec_read (term, search, replace); +} + +// Input: term the termination character +// search the character to search for +// replace the character with which to replace search +// Purpose: read in a file to a buffer using only a single dynamic +// allocation. +// Method: read until the local buffer is full and then recurse. +// Must continue until the termination character is reached. +// Allocate the final buffer based on the number of local +// buffers read and as the recursive calls bottom out, +// copy them in reverse order into the allocated buffer. + +char * +ACE_Read_Buffer::rec_read (int term, int search, int replace) +{ + ACE_TRACE ("ACE_Read_Buffer::rec_read"); + // This is our temporary workspace. + char buf[BUFSIZ]; + + int c = EOF; + size_t slot = 0; + int done = 0; + + // Read in the file char by char + while (slot < BUFSIZ) + { + c = getc (this->stream_); + + // Don't insert EOF into the buffer... + if (c == EOF) + { + ungetc (c, this->stream_); + break; + } + else if (c == term) + done = 1; + + // Check for possible substitutions. + if (c == search) + { + this->occurrences_++; + + if (replace >= 0) + c = replace; + } + + buf[slot++] = (char) c; + + // Substitutions must be made before checking for termination. + if (done) + break; + } + + // Increment the number of bytes. + this->size_ += slot; + + // Don't bother going any farther if the total size is 0. + if (this->size_ == 0) + return 0; + + char *result; + + // Recurse, when the recursion bottoms out, allocate the result + // buffer. + if (done || c == EOF) + { + // Use the allocator to acquire the memory. The + 1 allows + // space for the null terminator. + result = (char *) this->allocator_->malloc (this->size_ + 1); + + if (result == 0) + { + errno = ENOMEM; + return 0; + } + result += this->size_; + + // Null terminate the buffer. + *result = '\0'; + } + else if ((result = this->rec_read (term, search, replace)) == 0) + return 0; + + + // Copy buf into the appropriate location starting from end of + // buffer. Peter says this is confusing and that we should use + // memcpy() ;-) + for (size_t j = slot; j > 0; j--) + *--result = buf[j - 1]; + + return result; +} diff --git a/ace/Streams/Read_Buffer.h b/ace/Streams/Read_Buffer.h new file mode 100644 index 00000000000..ad1bef9fa32 --- /dev/null +++ b/ace/Streams/Read_Buffer.h @@ -0,0 +1,114 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Read_Buffer.h + * + * $Id$ + * + * @author Doug Schmidt and Seth Widoff + */ +//============================================================================= + + +#ifndef ACE_READ_BUFFER_H +#define ACE_READ_BUFFER_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Malloc.h" + +/** + * @class ACE_Read_Buffer + * + * @brief Efficiently reads an artibrarily large buffer from an input + * stream up to and including a termination character. Also + * performs search/replace on single occurrences a character in + * the buffer using the principles of Integrated Layer + * Processing. + * + * This implementation is optimized to do a single dynamic + * allocation and make only one copy of the data. It uses + * recursion and the run-time stack to accomplish this + * efficiently. + */ +class ACE_Export ACE_Read_Buffer +{ +public: + // = Initialization and termination methods. + /// Read from a FILE *. + ACE_Read_Buffer (FILE *fp, + int close_on_delete = 0, + ACE_Allocator * = 0); + + /// Read from an open HANDLE. + ACE_Read_Buffer (ACE_HANDLE handle, + int close_on_delete = 0, + ACE_Allocator * = 0); + + /// Closes the FILE *. + ~ACE_Read_Buffer (void); + + /** + * Returns a pointer dynamically allocated with + * to data from the input stream up to (and + * including) the . If is >= 0 then all + * occurrences of the value are substituted with the + * value. The last of the byte of data is a 0, so that + * can be used on it. The caller is responsible for + * freeing the pointer returned from this method using the + * . + */ + char *read (int terminator = EOF, + int search = '\n', + int replace = '\0'); + + /// Returns the number of characters replaced during a . + size_t replaced (void) const; + + /// Returns the size of the allocated buffer obtained during a + /// , not including the null terminator. + size_t size (void) const; + + /// Returns a pointer to its allocator. + ACE_Allocator *alloc (void) const; + + /// Dump the state of the object. + void dump (void) const; + +private: + /// Recursive helper method that does the work... + char *rec_read (int term, int search, int replace); + + /// The total number of characters in the buffer. + size_t size_; + + /// The total number of characters replaced. + size_t occurrences_; + + /// The stream we are reading from. + FILE *stream_; + + /// Keeps track of whether we should close the FILE in the + /// destructor. + int close_on_delete_; + + /// Pointer to the allocator. + ACE_Allocator *allocator_; + + // = Disallow copying and assignment... + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Read_Buffer &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Read_Buffer (const ACE_Read_Buffer &)) +}; + +#if defined (__ACE_INLINE__) +# include "ace/Read_Buffer.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_READ_BUFFER_H */ diff --git a/ace/Streams/Read_Buffer.i b/ace/Streams/Read_Buffer.i new file mode 100644 index 00000000000..3534c62db2e --- /dev/null +++ b/ace/Streams/Read_Buffer.i @@ -0,0 +1,28 @@ +/* -*- C++ -*- */ +// $Id$ + +// Accessor to the number of bytes in the buffer. + +ACE_INLINE size_t +ACE_Read_Buffer::size (void) const +{ + ACE_TRACE ("ACE_Read_Buffer::size"); + return this->size_; +} + +// The total number of characters replaced. + +ACE_INLINE size_t +ACE_Read_Buffer::replaced (void) const +{ + ACE_TRACE ("ACE_Read_Buffer::replaced"); + return this->occurrences_; +} + +ACE_INLINE ACE_Allocator * +ACE_Read_Buffer::alloc (void) const +{ + ACE_TRACE ("ACE_Read_Buffer::alloc"); + return this->allocator_; +} + diff --git a/ace/Streams/Stream.cpp b/ace/Streams/Stream.cpp new file mode 100644 index 00000000000..f72ebd4acfb --- /dev/null +++ b/ace/Streams/Stream.cpp @@ -0,0 +1,614 @@ +// Stream.cpp +// $Id$ + +#ifndef ACE_STREAM_C +#define ACE_STREAM_C + +//#include "ace/Module.h" +#include "ace/Stream.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Stream_Modules.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Stream.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Stream, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Stream) + +// Give some idea of what the heck is going on in a stream! + +template void +ACE_Stream::dump (void) const +{ + ACE_TRACE ("ACE_Stream::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("-------- module links --------\n"))); + + for (ACE_Module *mp = this->stream_head_; + ; + mp = mp->next ()) + { + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("module name = %s\n"), mp->name ())); + if (mp == this->stream_tail_) + break; + } + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("-------- writer links --------\n"))); + + ACE_Task *tp; + + for (tp = this->stream_head_->writer (); + ; + tp = tp->next ()) + { + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("writer queue name = %s\n"), tp->name ())); + tp->dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("-------\n"))); + if (tp == this->stream_tail_->writer () + || (this->linked_us_ + && tp == this->linked_us_->stream_head_->reader ())) + break; + } + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("-------- reader links --------\n"))); + for (tp = this->stream_tail_->reader (); ; tp = tp->next ()) + { + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("reader queue name = %s\n"), tp->name ())); + tp->dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("-------\n"))); + if (tp == this->stream_head_->reader () + || (this->linked_us_ + && tp == this->linked_us_->stream_head_->writer ())) + break; + } +} + +template int +ACE_Stream::push (ACE_Module *new_top) +{ + ACE_TRACE ("ACE_Stream::push"); + if (this->push_module (new_top, + this->stream_head_->next (), + this->stream_head_) == -1) + return -1; + else + return 0; +} + +template int +ACE_Stream::put (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Stream::put"); + return this->stream_head_->writer ()->put (mb, tv); +} + +template int +ACE_Stream::get (ACE_Message_Block *&mb, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Stream::get"); + return this->stream_head_->reader ()->getq (mb, tv); +} + +// Return the "top" ACE_Module in a ACE_Stream, skipping over the +// stream_head. + +template int +ACE_Stream::top (ACE_Module *&m) +{ + ACE_TRACE ("ACE_Stream::top"); + if (this->stream_head_->next () == this->stream_tail_) + return -1; + else + { + m = this->stream_head_->next (); + return 0; + } +} + +template int +ACE_Stream::insert (const ACE_TCHAR *prev_name, + ACE_Module *mod) +{ + ACE_TRACE ("ACE_Stream::insert"); + + for (ACE_Module *prev_mod = this->stream_head_; + prev_mod != 0; + prev_mod = prev_mod->next ()) + if (ACE_OS::strcmp (prev_mod->name (), prev_name) == 0) + { + ACE_Module *next_mod = prev_mod->next (); + + // We can't insert a module below . + if (next_mod == 0) + return -1; + + mod->link (next_mod); + prev_mod->link (mod); + + if (mod->reader ()->open (mod->arg ()) == -1) + return -1; + + if (mod->writer ()->open (mod->arg ()) == -1) + return -1; + + return 0; + } + + return -1; +} + +template int +ACE_Stream::replace (const ACE_TCHAR *replace_name, + ACE_Module *mod, + int flags) +{ + ACE_TRACE ("ACE_Stream::replace"); + ACE_Module *prev_mod = 0; + + for (ACE_Module *rep_mod = this->stream_head_; + rep_mod != 0; + rep_mod = rep_mod->next ()) + if (ACE_OS::strcmp (rep_mod->name (), replace_name) == 0) + { + ACE_Module *next_mod = rep_mod->next (); + + if (next_mod) + mod->link (next_mod); + else // In case the is . + { + mod->writer ()->next (0); + mod->next (0); + this->stream_tail_ = mod; + } + + if (prev_mod) + prev_mod->link (mod); + else // In case the is . + { + mod->reader ()->next (0); + this->stream_head_ = mod; + } + + if (mod->reader ()->open (mod->arg ()) == -1) + return -1; + + if (mod->writer ()->open (mod->arg ()) == -1) + return -1; + + if (flags != ACE_Module::M_DELETE_NONE) + { + rep_mod->close (flags); + delete rep_mod; + } + + return 0; + } + else + prev_mod = rep_mod; + + return -1; +} + +// Remove the "top" ACE_Module in a ACE_Stream, skipping over the +// stream_head. + +template int +ACE_Stream::pop (int flags) +{ + ACE_TRACE ("ACE_Stream::pop"); + if (this->stream_head_->next () == this->stream_tail_) + return -1; + else + { + // Skip over the ACE_Stream head. + ACE_Module *top_mod = this->stream_head_->next (); + ACE_Module *new_top = top_mod->next (); + + this->stream_head_->next (new_top); + + // Close the top ACE_Module. + + top_mod->close (flags); + + // Don't delete the Module unless the flags request this. + if (flags != ACE_Module::M_DELETE_NONE) + delete top_mod; + + this->stream_head_->writer ()->next (new_top->writer ()); + new_top->reader ()->next (this->stream_head_->reader ()); + return 0; + } +} + +// Remove a named ACE_Module from an arbitrary place in the +// ACE_Stream. + +template int +ACE_Stream::remove (const ACE_TCHAR *name, + int flags) +{ + ACE_TRACE ("ACE_Stream::remove"); + ACE_Module *prev = 0; + + for (ACE_Module *mod = this->stream_head_; + mod != 0; + mod = mod->next ()) + if (ACE_OS::strcmp (mod->name (), name) == 0) + { + if (prev == 0) // Deleting ACE_Stream Head + this->stream_head_->link (mod->next ()); + else + prev->link (mod->next ()); + + // Don't delete the Module unless the flags request this. + if (flags != ACE_Module::M_DELETE_NONE) + { + // Close down the module and release the memory. + mod->close (flags); + delete mod; + } + + return 0; + } + else + prev = mod; + + return -1; +} + +template ACE_Module * +ACE_Stream::find (const ACE_TCHAR *name) +{ + ACE_TRACE ("ACE_Stream::find"); + for (ACE_Module *mod = this->stream_head_; + mod != 0; + mod = mod->next ()) + if (ACE_OS::strcmp (mod->name (), name) == 0) + return mod; + + return 0; +} + +// Actually push a module onto the stack... + +template int +ACE_Stream::push_module (ACE_Module *new_top, + ACE_Module *current_top, + ACE_Module *head) +{ + ACE_TRACE ("ACE_Stream::push_module"); + ACE_Task *nt_reader = new_top->reader (); + ACE_Task *nt_writer = new_top->writer (); + ACE_Task *ct_reader = 0; + ACE_Task *ct_writer = 0; + + if (current_top) + { + ct_reader = current_top->reader (); + ct_writer = current_top->writer (); + ct_reader->next (nt_reader); + } + + nt_writer->next (ct_writer); + + if (head) + { + if (head != new_top) + head->link (new_top); + } + else + nt_reader->next (0); + + new_top->next (current_top); + + if (nt_reader->open (new_top->arg ()) == -1) + return -1; + + if (nt_writer->open (new_top->arg ()) == -1) + return -1; + return 0; +} + +template int +ACE_Stream::open (void *a, + ACE_Module *head, + ACE_Module *tail) +{ + ACE_TRACE ("ACE_Stream::open"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + ACE_Task *h1 = 0, *h2 = 0; + ACE_Task *t1 = 0, *t2 = 0; + + if (head == 0) + { + ACE_NEW_RETURN (h1, + ACE_Stream_Head, + -1); + ACE_NEW_RETURN (h2, + ACE_Stream_Head, + -1); + ACE_NEW_RETURN (head, + ACE_Module (ACE_LIB_TEXT ("ACE_Stream_Head"), + h1, h2, + a, + M_DELETE), + -1); + } + + if (tail == 0) + { + ACE_NEW_RETURN (t1, + ACE_Stream_Tail, + -1); + ACE_NEW_RETURN (t2, + ACE_Stream_Tail, + -1); + ACE_NEW_RETURN (tail, + ACE_Module (ACE_LIB_TEXT ("ACE_Stream_Tail"), + t1, t2, + a, + M_DELETE), + -1); + } + + // Make sure *all* the allocation succeeded! + if (head == 0 && (h1 == 0 || h2 == 0) + || tail == 0 && (t1 == 0 || t2 == 0)) + { + delete h1; + delete h2; + delete t1; + delete t2; + delete head; + delete tail; + errno = ENOMEM; + return -1; + } + + this->stream_head_ = head; + this->stream_tail_ = tail; + + if (this->push_module (this->stream_tail_) == -1) + return -1; + else if (this->push_module (this->stream_head_, + this->stream_tail_, + this->stream_head_) == -1) + return -1; + + return 0; +} + +template int +ACE_Stream::close (int flags) +{ + ACE_TRACE ("ACE_Stream::close"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + if (this->stream_head_ != 0 + && this->stream_tail_ != 0) + { + // Don't bother checking return value here. + this->unlink_i (); + + int result = 0; + + // Remove and cleanup all the intermediate modules. + + while (this->stream_head_->next () != this->stream_tail_) + if (this->pop (flags) == -1) + result = -1; + + // Clean up the head and tail of the stream. + if (this->stream_head_->close (flags) == -1) + result = -1; + if (this->stream_tail_->close (flags) == -1) + result = -1; + + // Cleanup the memory. + delete this->stream_head_; + delete this->stream_tail_; + + this->stream_head_ = 0; + this->stream_tail_ = 0; + + // Tell all threads waiting on the close that we are done. + this->final_close_.broadcast (); + return result; + } + return 0; +} + +template int +ACE_Stream::control (ACE_IO_Cntl_Msg::ACE_IO_Cntl_Cmds cmd, + void *a) +{ + ACE_TRACE ("ACE_Stream::control"); + ACE_IO_Cntl_Msg ioc (cmd); + + ACE_Message_Block *db; + + // Try to create a data block that contains the user-supplied data. + ACE_NEW_RETURN (db, + ACE_Message_Block (sizeof (int), + ACE_Message_Block::MB_IOCTL, + 0, + (char *) a), + -1); + // Try to create a control block that contains the control + // field and a pointer to the data block in 's continuation + // field. + ACE_Message_Block *cb = 0; + + ACE_NEW_RETURN (cb, + ACE_Message_Block (sizeof ioc, + ACE_Message_Block::MB_IOCTL, + db, + (char *) &ioc), + -1); + // @@ Michael: The old semantic assumed that cb returns == 0 + // if no memory was available. We will now return immediately + // without release (errno is set to ENOMEM by the macro). + + // If we can't allocate then we need to delete db and return + // -1. + if (cb == 0) + { + db->release (); + errno = ENOMEM; + return -1; + } + + int result; + + if (this->stream_head_->writer ()->put (cb) == -1) + result = -1; + else if (this->stream_head_->reader ()->getq (cb) == -1) + result = -1; + else + result = ((ACE_IO_Cntl_Msg *) cb->rd_ptr ())->rval (); + + // This will also release db if it's reference count == 0. + cb->release (); + + return result; +} + +// Link two streams together at their bottom-most Modules (i.e., the +// one just above the Stream tail). Note that all of this is premised +// on the fact that the Stream head and Stream tail are non-NULL... +// This must be called with locks held. + +template int +ACE_Stream::link_i (ACE_Stream &us) +{ + ACE_TRACE ("ACE_Stream::link_i"); + this->linked_us_ = &us; + // Make sure the other side is also linked to us! + us.linked_us_ = this; + + ACE_Module *my_tail = this->stream_head_; + + if (my_tail == 0) + return -1; + + // Locate the module just above our Stream tail. + while (my_tail->next () != this->stream_tail_) + my_tail = my_tail->next (); + + ACE_Module *other_tail = us.stream_head_; + + if (other_tail == 0) + return -1; + + // Locate the module just above the other Stream's tail. + while (other_tail->next () != us.stream_tail_) + other_tail = other_tail->next (); + + // Reattach the pointers so that the two streams are linked! + my_tail->writer ()->next (other_tail->reader ()); + other_tail->writer ()->next (my_tail->reader ()); + return 0; +} + +template int +ACE_Stream::link (ACE_Stream &us) +{ + ACE_TRACE ("ACE_Stream::link"); + + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + + return this->link_i (us); +} + +// Must be called with locks held... + +template int +ACE_Stream::unlink_i (void) +{ + ACE_TRACE ("ACE_Stream::unlink_i"); + + // Only try to unlink if we are in fact still linked! + + if (this->linked_us_ != 0) + { + ACE_Module *my_tail = this->stream_head_; + + // Only relink if we still exist! + if (my_tail) + { + // Find the module that's just before our stream tail. + while (my_tail->next () != this->stream_tail_) + my_tail = my_tail->next (); + + // Restore the writer's next() link to our tail. + my_tail->writer ()->next (this->stream_tail_->writer ()); + } + + ACE_Module *other_tail = + this->linked_us_->stream_head_; + + // Only fiddle with the other side if it in fact still remains. + if (other_tail != 0) + { + while (other_tail->next () != this->linked_us_->stream_tail_) + other_tail = other_tail->next (); + + other_tail->writer ()->next (this->linked_us_->stream_tail_->writer ()); + + } + + // Make sure the other side is also aware that it's been unlinked! + this->linked_us_->linked_us_ = 0; + + this->linked_us_ = 0; + return 0; + } + else + return -1; +} + +template int +ACE_Stream::unlink (void) +{ + ACE_TRACE ("ACE_Stream::unlink"); + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, ace_mon, this->lock_, -1); + return this->unlink_i (); +} + +template +ACE_Stream::ACE_Stream (void * a, + ACE_Module *head, + ACE_Module *tail) + : linked_us_ (0), + final_close_ (this->lock_) +{ + ACE_TRACE ("ACE_Stream::ACE_Stream"); + if (this->open (a, head, tail) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_Stream::open (%s, %s)\n"), + head->name (), tail->name ())); +} + +template +ACE_Stream::~ACE_Stream (void) +{ + ACE_TRACE ("ACE_Stream::~ACE_Stream"); + + if (this->stream_head_ != 0) + this->close (); +} + +template +ACE_Stream_Iterator::ACE_Stream_Iterator (const ACE_Stream &sr) + : next_ (sr.stream_head_) +{ + ACE_TRACE ("ACE_Stream_Iterator::ACE_Stream_Iterator"); +} + +#endif /* ACE_STREAM_C */ diff --git a/ace/Streams/Stream.h b/ace/Streams/Stream.h new file mode 100644 index 00000000000..f4f432e10f3 --- /dev/null +++ b/ace/Streams/Stream.h @@ -0,0 +1,232 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Stream.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_STREAM_H +#define ACE_STREAM_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/IO_Cntl_Msg.h" +#include "ace/Message_Block.h" +#include "ace/Time_Value.h" +#include "ace/Module.h" + +// Forward decls. +template class ACE_Stream_Iterator; + +/** + * @class ACE_Stream + * + * @brief This class is the primary abstraction for the ASX framework. + * It is moduled after System V Stream. + * + * A Stream consists of a stack of , each of which + * contains two . Even though the methods in this + * class are virtual, this class isn't really intended for + * subclassing unless you know what you are doing. In + * particular, the destructor calls , which + * won't be overridden properly unless you call it in a subclass + * destructor. + */ +template +class ACE_Stream +{ +public: + friend class ACE_Stream_Iterator; + + enum + { + /// Indicates that deletes the Tasks. Don't change this + /// value without updating the same enum in class ACE_Module... + M_DELETE = 3 + }; + + // = Initializatation and termination methods. + /** + * Create a Stream consisting of and as the Stream + * head and Stream tail, respectively. If these are 0 then the + * and are used, respectively. + * is the value past in to the methods of the tasks. + */ + ACE_Stream (void *arg = 0, + ACE_Module *head = 0, + ACE_Module *tail = 0); + + /** + * Create a Stream consisting of and as the Stream + * head and Stream tail, respectively. If these are 0 then the + * and are used, respectively. + * is the value past in to the methods of the tasks. + */ + virtual int open (void *arg, + ACE_Module *head = 0, + ACE_Module *tail = 0); + + /// Close down the stream and release all the resources. + virtual int close (int flags = M_DELETE); + + /// Close down the stream and release all the resources. + virtual ~ACE_Stream (void); + + // = ACE_Stream plumbing operations + + /// Add a new module right below the Stream head. + virtual int push (ACE_Module *mod); + + /// Remove the right below the Stream head and close it down. + virtual int pop (int flags = M_DELETE); + + /// Return the top module on the stream (right below the stream + /// head). + virtual int top (ACE_Module *&mod); + + /// Insert a new module below the named module . + virtual int insert (const ACE_TCHAR *prev_name, + ACE_Module *mod); + + /// Replace the named module with a new module . + virtual int replace (const ACE_TCHAR *replace_name, + ACE_Module *mod, + int flags = M_DELETE); + + /// Remove the named module from the stream. This bypasses the + /// strict LIFO ordering of and . + virtual int remove (const ACE_TCHAR *mod, + int flags = M_DELETE); + + /// Return current stream head. + virtual ACE_Module *head (void); + + /// Return current stream tail. + virtual ACE_Module *tail (void); + + /// Find a particular ACE_Module. + virtual ACE_Module *find (const ACE_TCHAR *mod); + + /// Create a pipe between two Streams. + virtual int link (ACE_Stream &); + + /// Remove a pipe formed between two Streams. + virtual int unlink (void); + + // = Blocking data transfer operations + /** + * Send the message down the stream, starting at the Module + * below the Stream head. Wait for upto amount of + * absolute time for the operation to complete (or block forever if + * == 0). + */ + virtual int put (ACE_Message_Block *mb, + ACE_Time_Value *timeout = 0); + + /** + * Read the message that is stored in the the stream head. + * Wait for upto amount of absolute time for the operation + * to complete (or block forever if == 0). + */ + virtual int get (ACE_Message_Block *&mb, + ACE_Time_Value *timeout = 0); + + /// Send control message down the stream. + virtual int control (ACE_IO_Cntl_Msg::ACE_IO_Cntl_Cmds cmd, + void *args); + + /// Synchronize with the final close of the stream. + virtual int wait (void); + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Actually perform the unlinking of two Streams (must be called + /// with locks held). + int unlink_i (void); + + /// Actually perform the linking of two Streams (must be called with + /// locks held). + int link_i (ACE_Stream &); + + /// Must a new module onto the Stream. + int push_module (ACE_Module *, + ACE_Module * = 0, + ACE_Module * = 0); + + /// Pointer to the head of the stream. + ACE_Module *stream_head_; + + /// Pointer to the tail of the stream. + ACE_Module *stream_tail_; + + /// Pointer to an adjoining linked stream. + ACE_Stream *linked_us_; + + // = Synchronization objects used for thread-safe streams. + /// Protect the stream against race conditions. + ACE_SYNCH_MUTEX_T lock_; + + /// Use to tell all threads waiting on the close that we are done. + ACE_SYNCH_CONDITION_T final_close_; +}; + +/** + * @class ACE_Stream_Iterator + * + * @brief Iterate through an . + */ +template +class ACE_Stream_Iterator +{ +public: + // = Initialization method. + ACE_Stream_Iterator (const ACE_Stream &sr); + + // = Iteration methods. + + /// Pass back the that hasn't been seen in the set. + /// Returns 0 when all items have been seen, else 1. + int next (const ACE_Module *&next_item); + + /// Returns 1 when all items have been seen, else 0. + int done (void) const; + + /// Move forward by one element in the set. Returns 0 when all the + /// items in the set have been seen, else 1. + int advance (void); + +private: + /// Next that we haven't yet seen. + ACE_Module *next_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Stream.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Stream.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Stream.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_STREAM_H */ diff --git a/ace/Streams/Stream.i b/ace/Streams/Stream.i new file mode 100644 index 00000000000..42a4989eff0 --- /dev/null +++ b/ace/Streams/Stream.i @@ -0,0 +1,49 @@ +/* -*- C++ -*- */ +// $Id$ + +// Stream.i + +template ACE_INLINE ACE_Module * +ACE_Stream::head (void) +{ + ACE_TRACE ("ACE_Stream::head"); + return this->stream_head_; +} + +template ACE_INLINE ACE_Module * +ACE_Stream::tail (void) +{ + ACE_TRACE ("ACE_Stream::tail"); + return this->stream_tail_; +} + +template ACE_INLINE int +ACE_Stream::wait (void) +{ + ACE_TRACE ("ACE_Stream::wait"); + return this->final_close_.wait (); +} + +template ACE_INLINE int +ACE_Stream_Iterator::next (const ACE_Module *&mod) +{ + ACE_TRACE ("ACE_Stream_Iterator::next"); + mod = this->next_; + return this->next_ != 0; +} + +template ACE_INLINE int +ACE_Stream_Iterator::done (void) const +{ + ACE_TRACE ("ACE_Stream_Iterator::done"); + return this->next_ == 0; +} + +template int +ACE_Stream_Iterator::advance (void) +{ + ACE_TRACE ("ACE_Stream_Iterator::advance"); + this->next_ = this->next_->next (); + return this->next_ != 0; +} + diff --git a/ace/Streams/Stream_Modules.cpp b/ace/Streams/Stream_Modules.cpp new file mode 100644 index 00000000000..859e2369839 --- /dev/null +++ b/ace/Streams/Stream_Modules.cpp @@ -0,0 +1,369 @@ +// Stream_Modules.cpp +// $Id$ + +#ifndef ACE_STREAM_MODULES_C +#define ACE_STREAM_MODULES_C + +#include "ace/Stream_Modules.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +ACE_RCSID(ace, Stream_Modules, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Stream_Head) + +template +ACE_Stream_Head::ACE_Stream_Head (void) +{ + ACE_TRACE ("ACE_Stream_Head::ACE_Stream_Head"); +} + +template +ACE_Stream_Head::~ACE_Stream_Head (void) +{ + ACE_TRACE ("ACE_Stream_Head::~ACE_Stream_Head"); +} + +template void +ACE_Stream_Head::dump (void) const +{ + ACE_TRACE ("ACE_Stream_Head::dump"); +} + +// ACE_Module that act as the head and tail of a Stream. + +template int +ACE_Stream_Head::open (void *) +{ + ACE_TRACE ("ACE_Stream_Head::open"); + return 0; +} + +template int +ACE_Stream_Head::close (u_long) +{ + ACE_TRACE ("ACE_Stream_Head::close"); + return 0; +} + +template int +ACE_Stream_Head::svc (void) +{ + ACE_TRACE ("ACE_Stream_Head::svc"); + return -1; +} + +template int +ACE_Stream_Head::control (ACE_Message_Block *mb) +{ + ACE_TRACE ("ACE_Stream_Head::control"); + ACE_IO_Cntl_Msg *ioc = (ACE_IO_Cntl_Msg *) mb->rd_ptr (); + ACE_IO_Cntl_Msg::ACE_IO_Cntl_Cmds cmd; + + switch (cmd = ioc->cmd ()) + { + case ACE_IO_Cntl_Msg::SET_LWM: + case ACE_IO_Cntl_Msg::SET_HWM: + this->water_marks (cmd, *(size_t *) mb->cont ()->rd_ptr ()); + ioc->rval (0); + break; + default: + return 0; + } + return ioc->rval (); +} + +// Performs canonical flushing at the ACE_Stream Head. + +template int +ACE_Stream_Head::canonical_flush (ACE_Message_Block *mb) +{ + ACE_TRACE ("ACE_Stream_Head::canonical_flush"); + char *cp = mb->rd_ptr (); + + if (ACE_BIT_ENABLED (*cp, ACE_Task_Flags::ACE_FLUSHR)) + { + this->flush (ACE_Task_Flags::ACE_FLUSHALL); + ACE_CLR_BITS (*cp, ACE_Task_Flags::ACE_FLUSHR); + } + + if (ACE_BIT_ENABLED (*cp, ACE_Task_Flags::ACE_FLUSHW)) + return this->reply (mb); + else + mb->release (); + return 0; +} + +template int +ACE_Stream_Head::put (ACE_Message_Block *mb, + ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Stream_Head::put"); + int res = 0; + + if (mb->msg_type () == ACE_Message_Block::MB_IOCTL + && (res = this->control (mb)) == -1) + return res; + + if (this->is_writer ()) + return this->put_next (mb, tv); + else // this->is_reader () + { + switch (mb->msg_type ()) + { + case ACE_Message_Block::MB_FLUSH: + return this->canonical_flush (mb); + default: + break; + } + + return this->putq (mb, tv); + } +} + +template int +ACE_Stream_Head::init (int, ACE_TCHAR *[]) +{ + ACE_TRACE ("ACE_Stream_Head::init"); + return 0; +} + +template int +ACE_Stream_Head::info (ACE_TCHAR **strp, size_t length) const +{ + ACE_TRACE ("ACE_Stream_Head::info"); + const ACE_TCHAR *name = this->name (); + + if (*strp == 0 && (*strp = ACE_OS::strdup (name)) == 0) + return -1; + else + ACE_OS::strsncpy (*strp, name, length); + return ACE_OS::strlen (name); +} + +template int +ACE_Stream_Head::fini (void) +{ + ACE_TRACE ("ACE_Stream_Head::fini"); + return 0; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Stream_Tail) + +template +ACE_Stream_Tail::ACE_Stream_Tail (void) +{ + ACE_TRACE ("ACE_Stream_Tail::ACE_Stream_Tail"); +} + +template +ACE_Stream_Tail::~ACE_Stream_Tail (void) +{ + ACE_TRACE ("ACE_Stream_Tail::~ACE_Stream_Tail"); +} + +template void +ACE_Stream_Tail::dump (void) const +{ + ACE_TRACE ("ACE_Stream_Tail::dump"); +} + +template int +ACE_Stream_Tail::open (void *) +{ + ACE_TRACE ("ACE_Stream_Tail::open"); + return 0; +} + +template int +ACE_Stream_Tail::close (u_long) +{ + ACE_TRACE ("ACE_Stream_Tail::close"); + return 0; +} + +template int +ACE_Stream_Tail::svc (void) +{ + ACE_TRACE ("ACE_Stream_Tail::svc"); + return -1; +} + +template int +ACE_Stream_Tail::control (ACE_Message_Block *mb) +{ + ACE_TRACE ("ACE_Stream_Tail::control"); + ACE_IO_Cntl_Msg *ioc = (ACE_IO_Cntl_Msg *) mb->rd_ptr (); + ACE_IO_Cntl_Msg::ACE_IO_Cntl_Cmds cmd; + + switch (cmd = ioc->cmd ()) + { + case ACE_IO_Cntl_Msg::SET_LWM: + case ACE_IO_Cntl_Msg::SET_HWM: + { + size_t wm_size = *(size_t *) mb->cont ()->rd_ptr (); + + this->water_marks (cmd, wm_size); + this->sibling ()->water_marks (cmd, wm_size); + ioc->rval (0); + break; + } + default: + mb->msg_type (ACE_Message_Block::MB_IOCNAK); + } + return this->reply (mb); +} + +// Perform flush algorithm as though we were the driver. + +template int +ACE_Stream_Tail::canonical_flush (ACE_Message_Block *mb) +{ + ACE_TRACE ("ACE_Stream_Tail::canonical_flush"); + char *cp = mb->rd_ptr (); + + if (ACE_BIT_ENABLED (*cp, ACE_Task_Flags::ACE_FLUSHW)) + { + this->flush (ACE_Task_Flags::ACE_FLUSHALL); + ACE_CLR_BITS (*cp, ACE_Task_Flags::ACE_FLUSHW); + } + + if (ACE_BIT_ENABLED (*cp, ACE_Task_Flags::ACE_FLUSHR)) + { + this->sibling ()->flush (ACE_Task_Flags::ACE_FLUSHALL); + return this->reply (mb); + } + else + mb->release (); + + return 0; +} + +template int +ACE_Stream_Tail::put (ACE_Message_Block *mb, + ACE_Time_Value *) +{ + ACE_TRACE ("ACE_Stream_Tail::put"); + + if (this->is_writer ()) + { + switch (mb->msg_type ()) + { + case ACE_Message_Block::MB_IOCTL: + return this->control (mb); + /* NOTREACHED */ + default: + mb->release (); + } + } + + return -1; +} + +template int +ACE_Stream_Tail::init (int, ACE_TCHAR *[]) +{ + ACE_TRACE ("ACE_Stream_Tail::init"); + return 0; +} + +template int +ACE_Stream_Tail::info (ACE_TCHAR **strp, size_t length) const +{ + ACE_TRACE ("ACE_Stream_Tail::info"); + const ACE_TCHAR *name = this->name (); + + if (*strp == 0 && (*strp = ACE_OS::strdup (name)) == 0) + return -1; + else + ACE_OS::strsncpy (*strp, name, length); + return ACE_OS::strlen (name); +} + +template int +ACE_Stream_Tail::fini (void) +{ + ACE_TRACE ("ACE_Stream_Tail::fini"); + return 0; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Thru_Task) + +template +ACE_Thru_Task::ACE_Thru_Task (void) +{ + ACE_TRACE ("ACE_Thru_Task::ACE_Thru_Task"); +} + +template +ACE_Thru_Task::~ACE_Thru_Task (void) +{ + ACE_TRACE ("ACE_Thru_Task::~ACE_Thru_Task"); +} + +template void +ACE_Thru_Task::dump (void) const +{ + ACE_TRACE ("ACE_Thru_Task::dump"); +} + +template int +ACE_Thru_Task::open (void *) +{ + ACE_TRACE ("ACE_Thru_Task::open"); + return 0; +} + +template int +ACE_Thru_Task::close (u_long) +{ + ACE_TRACE ("ACE_Thru_Task::close"); + return 0; +} + +template int +ACE_Thru_Task::svc (void) +{ + ACE_TRACE ("ACE_Thru_Task::svc"); + return -1; +} + +template int +ACE_Thru_Task::put (ACE_Message_Block *msg, + ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Thru_Task::put"); + return this->put_next (msg, tv); +} + +template int +ACE_Thru_Task::init (int, ACE_TCHAR *[]) +{ + ACE_TRACE ("ACE_Thru_Task::init"); + return 0; +} + +template int +ACE_Thru_Task::info (ACE_TCHAR **strp, + size_t length) const +{ + ACE_TRACE ("ACE_Thru_Task::info"); + const ACE_TCHAR *name = this->name (); + + if (*strp == 0 && (*strp = ACE_OS::strdup (name)) == 0) + return -1; + else + ACE_OS::strsncpy (*strp, name, length); + return ACE_OS::strlen (name); +} + +template int +ACE_Thru_Task::fini (void) +{ + ACE_TRACE ("ACE_Thru_Task::fini"); + return 0; +} + +#endif /* ACE_STREAM_MODULES_C */ diff --git a/ace/Streams/Stream_Modules.h b/ace/Streams/Stream_Modules.h new file mode 100644 index 00000000000..1cf2ef4e6ad --- /dev/null +++ b/ace/Streams/Stream_Modules.h @@ -0,0 +1,144 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Stream_Modules.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +// This needs to go outside of the #if !defined() block. Don't ask... +#include "ace/Task.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#ifndef ACE_STREAM_MODULES +#define ACE_STREAM_MODULES +#include "ace/pre.h" + +/** + * @class ACE_Stream_Head + * + * @brief Standard module that acts as the head of a ustream. + */ +template +class ACE_Stream_Head : public ACE_Task +{ +public: + /// Construction + ACE_Stream_Head (void); + + /// Destruction + ~ACE_Stream_Head (void); + + // = ACE_Task hooks + virtual int open (void *a = 0); + virtual int close (u_long flags = 0); + virtual int put (ACE_Message_Block *msg, ACE_Time_Value * = 0); + virtual int svc (void); + + // = Dynamic linking hooks + virtual int init (int argc, ACE_TCHAR *argv[]); + virtual int info (ACE_TCHAR **info_string, size_t length) const; + virtual int fini (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Performs canonical flushing at the ACE_Stream Head. + int control (ACE_Message_Block *); + int canonical_flush (ACE_Message_Block *); +}; + +/** + * @class ACE_Stream_Tail + * + * @brief Standard module that acts as the head of a ustream. + */ +template +class ACE_Stream_Tail : public ACE_Task +{ +public: + /// Construction + ACE_Stream_Tail (void); + + /// Destruction + ~ACE_Stream_Tail (void); + + // = ACE_Task hooks + virtual int open (void *a = 0); + virtual int close (u_long flags = 0); + virtual int put (ACE_Message_Block *msg, ACE_Time_Value * = 0); + virtual int svc (void); + + // = Dynamic linking hooks + virtual int init (int argc, ACE_TCHAR *argv[]); + virtual int info (ACE_TCHAR **info_string, size_t length) const; + virtual int fini (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Performs canonical flushing at the ACE_Stream tail. + int control (ACE_Message_Block *); + int canonical_flush (ACE_Message_Block *); +}; + +/** + * @class ACE_Thru_Task + * + * @brief Standard module that acts as a "no op", simply passing on all + * data to its adjacent neighbor. + */ +template +class ACE_Thru_Task : public ACE_Task +{ +public: + /// Construction + ACE_Thru_Task (void); + + /// Destruction + ~ACE_Thru_Task (void); + + // = ACE_Task hooks + virtual int open (void *a = 0); + virtual int close (u_long flags = 0); + virtual int put (ACE_Message_Block *msg, ACE_Time_Value * = 0); + virtual int svc (void); + + // = Dynamic linking hooks + virtual int init (int argc, ACE_TCHAR *argv[]); + virtual int info (ACE_TCHAR **info_string, size_t length) const; + virtual int fini (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Stream_Modules.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Stream_Modules.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_STREAM_MODULES */ diff --git a/ace/Streams/Task.cpp b/ace/Streams/Task.cpp new file mode 100644 index 00000000000..b073c27eaed --- /dev/null +++ b/ace/Streams/Task.cpp @@ -0,0 +1,227 @@ +// $Id$ + +#include "ace/Task.h" +#include "ace/Module.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Task.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Task, "$Id$") + +ACE_Task_Base::~ACE_Task_Base (void) +{ +} + +ACE_Task_Base::ACE_Task_Base (ACE_Thread_Manager *thr_man) + : thr_count_ (0), + thr_mgr_ (thr_man), + flags_ (0), + grp_id_ (-1) +{ +} + +// Wait for all threads running in a task to exit. + +int +ACE_Task_Base::wait (void) +{ + ACE_TRACE ("ACE_Task_Base::wait"); + + // If we don't have a thread manager, we probably were never + // activated. + if (this->thr_mgr () != 0) + return this->thr_mgr ()->wait_task (this); + else + return 0; +} + +// Suspend a task. +int +ACE_Task_Base::suspend (void) +{ + ACE_TRACE ("ACE_Task_Base::suspend"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + if (this->thr_count_ > 0) + return this->thr_mgr_->suspend_task (this); + + return 0; +} + +// Resume a suspended task. +int +ACE_Task_Base::resume (void) +{ + ACE_TRACE ("ACE_Task_Base::resume"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + if (this->thr_count_ > 0) + return this->thr_mgr_->resume_task (this); + + return 0; +} + +int +ACE_Task_Base::activate (long flags, + int n_threads, + int force_active, + long priority, + int grp_id, + ACE_Task_Base *task, + ACE_hthread_t thread_handles[], + void *stack[], + size_t stack_size[], + ACE_thread_t thread_ids[]) +{ + ACE_TRACE ("ACE_Task_Base::activate"); + +#if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + + // If the task passed in is zero, we will use + if (task == 0) + task = this; + + if (this->thr_count_ > 0 && force_active == 0) + return 1; // Already active. + else + { + if (this->thr_count_ > 0 && grp_id != -1) + // If we're joining an existing group of threads then make + // sure to use its group id. + grp_id = this->grp_id_; + this->thr_count_ += n_threads; + } + + // Use the ACE_Thread_Manager singleton if we're running as an + // active object and the caller didn't supply us with a + // Thread_Manager. + if (this->thr_mgr_ == 0) +# if defined (ACE_THREAD_MANAGER_LACKS_STATICS) + this->thr_mgr_ = ACE_THREAD_MANAGER_SINGLETON::instance (); +# else /* ! ACE_THREAD_MANAGER_LACKS_STATICS */ + this->thr_mgr_ = ACE_Thread_Manager::instance (); +# endif /* ACE_THREAD_MANAGER_LACKS_STATICS */ + + int grp_spawned = -1; + if (thread_ids == 0) + // Thread Ids were not specified + grp_spawned = + this->thr_mgr_->spawn_n (n_threads, + ACE_THR_FUNC (&ACE_Task_Base::svc_run), + (void *) this, + flags, + priority, + grp_id, + task, + thread_handles, + stack, + stack_size); + else + // thread names were specified + grp_spawned = + this->thr_mgr_->spawn_n (thread_ids, + n_threads, + ACE_THR_FUNC (&ACE_Task_Base::svc_run), + (void *) this, + flags, + priority, + grp_id, + stack, + stack_size, + thread_handles, + task); + if (grp_spawned == -1) + { + // If spawn_n fails, restore original thread count. + this->thr_count_ -= n_threads; + return -1; + } + + if (this->grp_id_ == -1) + this->grp_id_ = grp_spawned; + + return 0; + +#else + { + // Keep the compiler from complaining. + ACE_UNUSED_ARG (flags); + ACE_UNUSED_ARG (n_threads); + ACE_UNUSED_ARG (force_active); + ACE_UNUSED_ARG (priority); + ACE_UNUSED_ARG (grp_id); + ACE_UNUSED_ARG (task); + ACE_UNUSED_ARG (thread_handles); + ACE_UNUSED_ARG (stack); + ACE_UNUSED_ARG (stack_size); + ACE_UNUSED_ARG (thread_ids); + ACE_NOTSUP_RETURN (-1); + } +#endif /* ACE_MT_SAFE */ +} + +void +ACE_Task_Base::cleanup (void *object, void *) +{ + ACE_Task_Base *t = (ACE_Task_Base *) object; + + // The thread count must be decremented first in case the + // hook does something crazy like "delete this". + t->thr_count_dec (); + + // @@ Is it possible to pass in the exit status somehow? + t->close (); +} + + +#if defined (ACE_HAS_SIG_C_FUNC) +extern "C" void +ACE_Task_Base_cleanup (void *object, void *) +{ + ACE_Task_Base::cleanup (object, 0); +} +#endif /* ACE_HAS_SIG_C_FUNC */ + +void * +ACE_Task_Base::svc_run (void *args) +{ + ACE_TRACE ("ACE_Task_Base::svc_run"); + + ACE_Task_Base *t = (ACE_Task_Base *) args; + + // Register ourself with our 's thread exit hook + // mechanism so that our close() hook will be sure to get invoked + // when this thread exits. + +#if defined ACE_HAS_SIG_C_FUNC + t->thr_mgr ()->at_exit (t, ACE_Task_Base_cleanup, 0); +#else + t->thr_mgr ()->at_exit (t, ACE_Task_Base::cleanup, 0); +#endif /* ACE_HAS_SIG_C_FUNC */ + + // Call the Task's svc() hook method. + void * status = ACE_reinterpret_cast(void *, t->svc ()); + +// If we changed this zero change the other if in OS.cpp Thread_Adapter::invoke +#if 1 + // Call the close> hook. + ACE_Thread_Manager *thr_mgr_ptr = t->thr_mgr (); + + // This calls the Task->close () hook. + t->cleanup (t, 0); + + // This prevents a second invocation of the cleanup code + // (called later by . + thr_mgr_ptr->at_exit (t, 0, 0); +#endif + return status; +} + +// Forward the call to close() so that existing applications don't +// break. + +int +ACE_Task_Base::module_closed (void) +{ + return this->close (1); +} diff --git a/ace/Streams/Task.h b/ace/Streams/Task.h new file mode 100644 index 00000000000..28e02dc6bb9 --- /dev/null +++ b/ace/Streams/Task.h @@ -0,0 +1,265 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Task.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_TASK_H +#define ACE_TASK_H +#include "ace/pre.h" + +#include "ace/Service_Object.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Thread_Manager.h" + +/** + * @class ACE_Task_Flags + * + * @brief These flags are used within the ACE_Task. + * + * These flags should be hidden within ACE_Task. Unfortunately, the + * HP/UX C++ compiler can't grok this... Fortunately, there's no + * code defined here, so we don't have to worry about multiple + * definitions. + */ +class ACE_Export ACE_Task_Flags +{ +public: + enum + { + /// Identifies a Task as being the "reader" in a Module. + ACE_READER = 01, + /// Just flush data messages in the queue. + ACE_FLUSHDATA = 02, + /// Flush all messages in the Queue. + ACE_FLUSHALL = 04, + /// flush read queue + ACE_FLUSHR = 010, + /// flush write queue + ACE_FLUSHW = 020, + /// flush both queues + ACE_FLUSHRW = 030 + }; +}; + +/** + * @class ACE_Task_Base + * + * @brief Direct base class for the ACE_Task template. + * + * This class factors out the non-template code in order to + * reduce template bloat, as well as to make it possible for the + * to store *'s + * polymorphically. + */ +class ACE_Export ACE_Task_Base : public ACE_Service_Object +{ +public: + // = Initialization and termination methods. + /// Constructor. + ACE_Task_Base (ACE_Thread_Manager * = 0); + + /// Destructor. + virtual ~ACE_Task_Base (void); + + // = Initialization and termination hooks. + + // These methods should be overridden by subclasses if you'd like to + // provide -specific initialization and termination behavior. + + /// Hook called to open a Task. can be used to pass arbitrary + /// information into . + virtual int open (void *args = 0); + + /** + * Hook called from when during thread exit and from + * the default implemenation of . In general, this + * method shouldn't be called directly by an application, + * particularly if the is running as an Active Object. + * Instead, a special message should be passed into the via + * the method defined below, and the method should + * interpret this as a flag to shut down the . + */ + virtual int close (u_long flags = 0); + + /** + * Hook called during . The default + * implementation calls forwards the call to close(1). Please + * notice the changed value of the default argument of . + * This allows tasks to differ between the call has been originated + * from or from . Be aware that + * close(0) will be also called when a thread associated with the + * ACE_Task instance exits. + */ + virtual int module_closed (void); + + // = Immediate and deferred processing methods, respectively. + + // These methods should be overridden by subclasses if you'd like to + // provide -specific message processing behavior. + + /// A hook method that can be used to pass a message to a + /// task, where it can be processed immediately or queued for subsequent + /// processing in the hook method. + virtual int put (ACE_Message_Block *, ACE_Time_Value * = 0); + + /// Run by a daemon thread to handle deferred processing. + virtual int svc (void); + + // = Active object activation method. + /** + * Turn the task into an active object, i.e., having of + * control, all running at the level (see below) with the + * same , all of which invoke . Returns -1 if + * failure occurs, returns 1 if Task is already an active object and + * is false (i.e., do *not* create a new thread in + * this case), and returns 0 if Task was not already an active + * object and a thread is created successfully or thread is an + * active object and is true. Note that if + * is true and there are already threads spawned in + * this , the parameter is ignored and the + * of any newly activated thread(s) will inherit the existing + * of the existing thread(s) in the . + * + * The <{flags}> are a bitwise-OR of the following: + * = BEGIN + * THR_CANCEL_DISABLE, THR_CANCEL_ENABLE, THR_CANCEL_DEFERRED, + * THR_CANCEL_ASYNCHRONOUS, THR_BOUND, THR_NEW_LWP, THR_DETACHED, + * THR_SUSPENDED, THR_DAEMON, THR_JOINABLE, THR_SCHED_FIFO, + * THR_SCHED_RR, THR_SCHED_DEFAULT, THR_EXPLICIT_SCHED, + * THR_SCOPE_SYSTEM, THR_SCOPE_PROCESS + * = END + * + * By default, or if <{priority}> is set to + * ACE_DEFAULT_THREAD_PRIORITY, an "appropriate" priority value for + * the given scheduling policy (specified in <{flags}>, e.g., + * ) is used. This value is calculated + * dynamically, and is the median value between the minimum and + * maximum priority values for the given policy. If an explicit + * value is given, it is used. Note that actual priority values are + * EXTREMEMLY implementation-dependent, and are probably best + * avoided. + * + * If != 0 it is assumed to be an array of + * thread_handles that will be assigned the values of the thread + * handles being spawned. Returns -1 on failure ( will + * explain...), otherwise returns the group id of the threads. + * + * If != 0 it is assumed to be an array of pointers to + * the base of the stacks to use for the threads being spawned. + * Likewise, if != 0 it is assumed to be an array of + * values indicating how big each of the corresponding s + * are. + */ + virtual int activate (long flags = THR_NEW_LWP | THR_JOINABLE, + int n_threads = 1, + int force_active = 0, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + int grp_id = -1, + ACE_Task_Base *task = 0, + ACE_hthread_t thread_handles[] = 0, + void *stack[] = 0, + size_t stack_size[] = 0, + ACE_thread_t thread_ids[] = 0); + + /// Wait for all threads running in this task to exit. + virtual int wait (void); + + // = Suspend/resume a Task. + + // Note that these methods are not portable and should be avoided + // since they are inherently error-prone to use. They are only here + // for (the rare) applications that know how to use them correctly. + /// Suspend a task. + /// Resume a suspended task. + virtual int suspend (void); + virtual int resume (void); + + /// Get the current group id. + int grp_id (void) const; + + /// Set the current group id. + void grp_id (int); + + /// Gets the thread manager associated with this Task. + ACE_Thread_Manager *thr_mgr (void) const; + + /// Set the thread manager associated with this Task. + void thr_mgr (ACE_Thread_Manager *); + + /// True if queue is a reader, else false. + int is_reader (void) const; + + /// True if queue is a writer, else false. + int is_writer (void) const; + + /** + * Returns the number of threads currently running within a task. + * If we're a passive object this value is 0, else it's greater than + * 0. + */ + size_t thr_count (void) const; + + /// Atomically decrement the thread count by 1. This should only be + /// called by the class destructor. + void thr_count_dec (void); + + /// Routine that runs the service routine as a daemon thread. + static void *svc_run (void *); + + /// Cleanup hook that is called when a thread exits to gracefully + /// shutdown an . + static void cleanup (void *object, void *params); + + // = Internal data (should be private...). +// private: + + /** + * Count of the number of threads running within the task. If this + * value is great than 0 then we're an active object and the value + * of is the number of active threads at this instant. + * If the value == 0, then we're a passive object. + */ + size_t thr_count_; + + /// Multi-threading manager. + ACE_Thread_Manager *thr_mgr_; + + /// ACE_Task flags. + u_long flags_; + + /// This maintains the group id of the Task. + int grp_id_; + +#if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) + /// Protect the state of a Task during concurrent operations, but + /// only if we're configured as MT safe... + ACE_Thread_Mutex lock_; +#endif /* ACE_MT_SAFE */ + +private: + + // = Disallow these operations. + ACE_Task_Base &operator= (const ACE_Task_Base &); + ACE_Task_Base (const ACE_Task_Base &); +}; + +#if defined (__ACE_INLINE__) +#include "ace/Task.i" +#endif /* __ACE_INLINE__ */ + +// Include the ACE_Task templates classes at this point. +#include "ace/Task_T.h" + +#include "ace/post.h" +#endif /* ACE_TASK_H */ diff --git a/ace/Streams/Task.i b/ace/Streams/Task.i new file mode 100644 index 00000000000..fd9f992579b --- /dev/null +++ b/ace/Streams/Task.i @@ -0,0 +1,114 @@ +/* -*- C++ -*- */ +// $Id$ + +// Task.i + +ACE_INLINE ACE_Thread_Manager * +ACE_Task_Base::thr_mgr (void) const +{ + ACE_TRACE ("ACE_Task_Base::thr_mgr"); + return this->thr_mgr_; +} + +ACE_INLINE void +ACE_Task_Base::thr_mgr (ACE_Thread_Manager *thr_mgr) +{ + ACE_TRACE ("ACE_Task_Base::thr_mgr"); + this->thr_mgr_ = thr_mgr; +} + +// Return the count of the current number of threads. +ACE_INLINE size_t +ACE_Task_Base::thr_count (void) const +{ + ACE_TRACE ("ACE_Task_Base::thr_count"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->lock_, 0)); + + return this->thr_count_; +} + +// Decrement the count of the active threads by 1. + +ACE_INLINE void +ACE_Task_Base::thr_count_dec (void) +{ + ACE_TRACE ("ACE_Task_Base::thr_count_dec"); + ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_mon, this->lock_)); + + this->thr_count_--; +} + +ACE_INLINE int +ACE_Task_Base::is_reader (void) const +{ + ACE_TRACE ("ACE_Task_Base::is_reader"); + return (ACE_BIT_ENABLED (this->flags_, ACE_Task_Flags::ACE_READER)); +} + +ACE_INLINE int +ACE_Task_Base::is_writer (void) const +{ + ACE_TRACE ("ACE_Task_Base::is_writer"); + return (ACE_BIT_DISABLED (this->flags_, ACE_Task_Flags::ACE_READER)); +} + +// Default ACE_Task service routine + +ACE_INLINE int +ACE_Task_Base::svc (void) +{ + ACE_TRACE ("ACE_Task_Base::svc"); + return 0; +} + +// Default ACE_Task open routine + +ACE_INLINE int +ACE_Task_Base::open (void *) +{ + ACE_TRACE ("ACE_Task_Base::open"); + return 0; +} + +// Default ACE_Task close routine + +ACE_INLINE int +ACE_Task_Base::close (u_long) +{ + ACE_TRACE ("ACE_Task_Base::close"); + return 0; +} + +// Default ACE_Task put routine. + +ACE_INLINE int +ACE_Task_Base::put (ACE_Message_Block *, ACE_Time_Value *) +{ + ACE_TRACE ("ACE_Task_Base::put"); + return 0; +} + +// Get the current group id. +ACE_INLINE int +ACE_Task_Base::grp_id (void) const +{ + ACE_TRACE ("ACE_Task_Base::grp_id"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->lock_, -1)); + return this->grp_id_; +} + +// Set the current group id. + +ACE_INLINE void +ACE_Task_Base::grp_id (int id) +{ + ACE_TRACE ("ACE_Task_Base::grp_id"); + ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_mon, this->lock_)); + + // Cache the group id in the task and then set it in the + // Thread_Manager, if there is one. + this->grp_id_ = id; + if (this->thr_mgr ()) + this->thr_mgr ()->set_grp (this, id); +} + diff --git a/ace/Streams/Task_T.cpp b/ace/Streams/Task_T.cpp new file mode 100644 index 00000000000..88bc3a2bfc5 --- /dev/null +++ b/ace/Streams/Task_T.cpp @@ -0,0 +1,105 @@ +// Task.cpp +// $Id$ + +#ifndef ACE_TASK_T_C +#define ACE_TASK_T_C + +#include "ace/Task_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Module.h" +//#include "ace/Service_Config.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Task_T.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Task_T, "$Id$") + +template void +ACE_Task::dump (void) const +{ + ACE_TRACE ("ACE_Task::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_mgr_ = %x"), this->thr_mgr_)); + this->msg_queue_->dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("delete_msg_queue_ = %d\n"), this->delete_msg_queue_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nflags = %x"), this->flags_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nmod_ = %x"), this->mod_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nnext_ = %x"), this->next_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ngrp_id_ = %d"), this->grp_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_count_ = %d"), this->thr_count_)); +#if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) + this->lock_.dump (); +#endif /* ACE_MT_SAFE */ + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +// If the user doesn't supply a ACE_Message_Queue pointer then we'll +// allocate one dynamically. Otherwise, we'll use the one they give. + +template +ACE_Task::ACE_Task (ACE_Thread_Manager *thr_man, + ACE_Message_Queue *mq) + : ACE_Task_Base (thr_man), + msg_queue_ (0), + delete_msg_queue_ (0), + mod_ (0), + next_ (0) +{ + ACE_TRACE ("ACE_Task::ACE_Task"); + + if (mq == 0) + { + ACE_NEW (mq, + ACE_Message_Queue); + this->delete_msg_queue_ = 1; + } + + this->msg_queue_ = mq; +} + +template +ACE_Task::~ACE_Task (void) +{ + ACE_TRACE ("ACE_Task::~ACE_Task"); + if (this->delete_msg_queue_) + delete this->msg_queue_; + + // These assignments aren't strickly necessary but they help guard + // against odd race conditions... + this->delete_msg_queue_ = 0; +} + +template ACE_Task * +ACE_Task::sibling (void) +{ + ACE_TRACE ("ACE_Task::sibling"); + if (this->mod_ == 0) + return 0; + else + return this->mod_->sibling (this); +} + +template const ACE_TCHAR * +ACE_Task::name (void) const +{ + ACE_TRACE ("ACE_Task::name"); + if (this->mod_ == 0) + return 0; + else + return this->mod_->name (); +} + +template ACE_Module * +ACE_Task::module (void) const +{ + ACE_TRACE ("ACE_Task::module"); + return this->mod_; +} + +#endif /* ACE_TASK_T_C */ diff --git a/ace/Streams/Task_T.h b/ace/Streams/Task_T.h new file mode 100644 index 00000000000..b7e5a14b7c2 --- /dev/null +++ b/ace/Streams/Task_T.h @@ -0,0 +1,174 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Task_T.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + + +#ifndef ACE_TASK_T_H +#define ACE_TASK_T_H +#include "ace/pre.h" + +#include "ace/Message_Queue.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Synch_T.h" +#include "ace/Task.h" + +// Forward decls... +template class ACE_Module; + +/** + * @class ACE_Task + * + * @brief Primary interface for application message processing, as well + * as input and output message queueing. + * + * This class serves as the basis for passive and active objects + * in ACE. + */ +template +class ACE_Export ACE_Task : public ACE_Task_Base +{ +public: + friend class ACE_Module; + friend class ACE_Module_Type; + + // = Initialization/termination methods. + /** + * Initialize a Task, supplying a thread manager and a message + * queue. If the user doesn't supply a ACE_Message_Queue pointer + * then we'll allocate one dynamically. Otherwise, we'll use the + * one passed as a parameter. + */ + ACE_Task (ACE_Thread_Manager *thr_mgr = 0, + ACE_Message_Queue *mq = 0); + + /// Destructor. + virtual ~ACE_Task (void); + + /// Gets the message queue associated with this task. + ACE_Message_Queue *msg_queue (void); + + /// Sets the message queue associated with this task. + void msg_queue (ACE_Message_Queue *); + +public: // Should be protected: + // = Message queue manipulation methods. + + // = Enqueue and dequeue methods. + + // For the following five method if == 0, the caller will + // block until action is possible, else will wait until the + // <{absolute}> time specified in * elapses). These calls + // will return, however, when queue is closed, deactivated, when a + // signal occurs, or if the time specified in timeout elapses, (in + // which case errno = EWOULDBLOCK). + + /// Insert message into the message queue. Note that uses + /// <{absolute}> time rather than <{relative}> time. + int putq (ACE_Message_Block *, ACE_Time_Value *timeout = 0); + + /// Extract the first message from the queue (blocking). Note that + /// uses <{absolute}> time rather than <{relative}> time. + int getq (ACE_Message_Block *&mb, ACE_Time_Value *timeout = 0); + + /// Return a message to the queue. Note that uses + /// <{absolute}> time rather than <{relative}> time. + int ungetq (ACE_Message_Block *, ACE_Time_Value *timeout = 0); + + /** + * Turn the message around and send it back down the Stream. Note + * that uses <{absolute}> time rather than <{relative}> + * time. + */ + int reply (ACE_Message_Block *, ACE_Time_Value *timeout = 0); + + /** + * Transfer message to the adjacent ACE_Task in a ACE_Stream. Note + * that uses <{absolute}> time rather than <{relative}> + * time. + */ + int put_next (ACE_Message_Block *msg, ACE_Time_Value *timeout = 0); + + /// Tests whether we can enqueue a message without blocking. + int can_put (ACE_Message_Block *); + + // = ACE_Task utility routines to identify names et al. + /// Return the name of the enclosing Module if there's one associated + /// with the Task, else returns 0. + const ACE_TCHAR *name (void) const; + + // = Pointers to next ACE_Task_Base (if ACE is part of an ACE_Stream). + /// Get next Task pointer. + /// Set next Task pointer. + ACE_Task *next (void); + void next (ACE_Task *); + + /// Return the Task's sibling if there's one associated with the + /// Task's Module, else returns 0. + ACE_Task *sibling (void); + + /// Return the Task's Module if there is one, else returns 0. + ACE_Module *module (void) const; + + /** + * Flush the queue. Note that if this conflicts with the C++ + * iostream function, just rewrite the iostream function as + * ::. + */ + int flush (u_long flag = ACE_Task_Flags::ACE_FLUSHALL); + + // = Special routines corresponding to certain message types. + + /// Manipulate watermarks. + void water_marks (ACE_IO_Cntl_Msg::ACE_IO_Cntl_Cmds, size_t); + + /// Queue of messages on the ACE_Task.. + ACE_Message_Queue *msg_queue_; + + /// 1 if should delete Message_Queue, 0 otherwise. + int delete_msg_queue_; + + /// Back-pointer to the enclosing module. + ACE_Module *mod_; + + /// Pointer to adjacent ACE_Task. + ACE_Task *next_; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + + // = Disallow these operations. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Task &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Task (const ACE_Task &)) +}; + +#if defined (__ACE_INLINE__) +#include "ace/Task_T.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Task_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Task_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TASK_T_H */ diff --git a/ace/Streams/Task_T.i b/ace/Streams/Task_T.i new file mode 100644 index 00000000000..1a78650279f --- /dev/null +++ b/ace/Streams/Task_T.i @@ -0,0 +1,103 @@ +/* -*- C++ -*- */ +// $Id$ + +// Task_T.i + +template ACE_INLINE void +ACE_Task::water_marks (ACE_IO_Cntl_Msg::ACE_IO_Cntl_Cmds cmd, + size_t wm_size) +{ + ACE_TRACE ("ACE_Task::water_marks"); + if (cmd == ACE_IO_Cntl_Msg::SET_LWM) + this->msg_queue_->low_water_mark (wm_size); + else /* cmd == ACE_IO_Cntl_Msg::SET_HWM */ + this->msg_queue_->high_water_mark (wm_size); +} + +template ACE_INLINE int +ACE_Task::getq (ACE_Message_Block *&mb, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Task::getq"); + return this->msg_queue_->dequeue_head (mb, tv); +} + +template ACE_INLINE int +ACE_Task::can_put (ACE_Message_Block *) +{ + ACE_TRACE ("ACE_Task::can_put"); + assert (!"not implemented"); + return -1; +} + +template ACE_INLINE int +ACE_Task::putq (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Task::putq"); + return this->msg_queue_->enqueue_tail (mb, tv); +} + +template ACE_INLINE int +ACE_Task::ungetq (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Task::ungetq"); + return this->msg_queue_->enqueue_head (mb, tv); +} + +template ACE_INLINE int +ACE_Task::flush (u_long flag) +{ + ACE_TRACE ("ACE_Task::flush"); + if (ACE_BIT_ENABLED (flag, ACE_Task_Flags::ACE_FLUSHALL)) + return this->msg_queue_ != 0 && this->msg_queue_->close (); + else + return -1; // Note, need to be more careful about what we free... +} + +template ACE_INLINE void +ACE_Task::msg_queue (ACE_Message_Queue *mq) +{ + ACE_TRACE ("ACE_Task::msg_queue"); + if (this->delete_msg_queue_) + { + delete this->msg_queue_; + this->delete_msg_queue_ = 0; + } + this->msg_queue_ = mq; +} + +template ACE_Message_Queue * +ACE_Task::msg_queue (void) +{ + ACE_TRACE ("ACE_Task::msg_queue"); + return this->msg_queue_; +} + +template ACE_INLINE int +ACE_Task::reply (ACE_Message_Block *mb, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Task::reply"); + return this->sibling ()->put_next (mb, tv); +} + +template ACE_INLINE ACE_Task * +ACE_Task::next (void) +{ + ACE_TRACE ("ACE_Task::next"); + return this->next_; +} + +template ACE_INLINE void +ACE_Task::next (ACE_Task *q) +{ + ACE_TRACE ("ACE_Task::next"); + this->next_ = q; +} + +// Transfer msg to the next ACE_Task. + +template ACE_INLINE int +ACE_Task::put_next (ACE_Message_Block *msg, ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_Task::put_next"); + return this->next_ == 0 ? -1 : this->next_->put (msg, tv); +} diff --git a/ace/Streams/Typed_SV_Message.cpp b/ace/Streams/Typed_SV_Message.cpp new file mode 100644 index 00000000000..6b4ae9024b0 --- /dev/null +++ b/ace/Streams/Typed_SV_Message.cpp @@ -0,0 +1,26 @@ +// Typed_SV_Message.cpp +// $Id$ + +#ifndef ACE_TYPED_SV_MESSAGE_C +#define ACE_TYPED_SV_MESSAGE_C +#include "ace/Typed_SV_Message.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if !defined (__ACE_INLINE__) +#include "ace/Typed_SV_Message.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Typed_SV_Message, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Typed_SV_Message) + +template void +ACE_Typed_SV_Message::dump (void) const +{ + ACE_TRACE ("ACE_Typed_SV_Message::dump"); +} + +#endif /* ACE_TYPED_SV_MESSAGE_C */ diff --git a/ace/Streams/Typed_SV_Message.h b/ace/Streams/Typed_SV_Message.h new file mode 100644 index 00000000000..36c5137b80e --- /dev/null +++ b/ace/Streams/Typed_SV_Message.h @@ -0,0 +1,94 @@ +/* -*- C++ -*- */ + + +//============================================================================= +/** + * @file Typed_SV_Message.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_TYPED_SV_MESSAGE_H +#define ACE_TYPED_SV_MESSAGE_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_Typed_SV_Message + * + * @brief Defines the header file for the C++ wrapper for System V + * message queues. + */ +template +class ACE_Typed_SV_Message +{ +public: + // = Initialization and termination methods. + ACE_Typed_SV_Message (long type = 0, + int length = sizeof (T), + int max_size = sizeof (T)); + ACE_Typed_SV_Message (const T &data, + long type = 0, + int length = sizeof (T), + int max_size = sizeof (T)); + ~ACE_Typed_SV_Message (void); + + // = Get/set the type of the message. + long type (void) const; + void type (long type); + + // = Get/set the length of the message. + int length (void) const; + void length (int l); + + // = Get/set the maximum size of the message. + int max_size (void) const; + void max_size (int m); + + // = Get/set a pointer to the data in the message. + T &data (void); + void data (const T &data); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Type of message. + long type_; + + /// Length of this message. + int length_; + + /// Maximum length of any message. + int max_; + + /// Data stored in a message. + T data_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Typed_SV_Message.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Typed_SV_Message.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Typed_SV_Message.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TYPED_SV_MESSAGE_H */ diff --git a/ace/Streams/Typed_SV_Message.i b/ace/Streams/Typed_SV_Message.i new file mode 100644 index 00000000000..4d74a14f5dd --- /dev/null +++ b/ace/Streams/Typed_SV_Message.i @@ -0,0 +1,91 @@ +/* -*- C++ -*- */ +// $Id$ + +// Typed_SV_Message.i + +template ACE_INLINE +ACE_Typed_SV_Message::ACE_Typed_SV_Message (long t, + int l, + int m) + : type_ (t) +{ + ACE_TRACE ("ACE_Typed_SV_Message::ACE_Typed_SV_Message"); + this->length (l); + this->max_size (m); +} + +template ACE_INLINE +ACE_Typed_SV_Message::ACE_Typed_SV_Message (const T &d, + long t, + int l, + int m) + : type_ (t), + data_ (d) +{ + ACE_TRACE ("ACE_Typed_SV_Message::ACE_Typed_SV_Message"); + this->length (l); + this->max_size (m); +} + +template ACE_INLINE +ACE_Typed_SV_Message::~ACE_Typed_SV_Message (void) +{ + ACE_TRACE ("ACE_Typed_SV_Message::~ACE_Typed_SV_Message"); +} + +template ACE_INLINE long +ACE_Typed_SV_Message::type (void) const +{ + ACE_TRACE ("ACE_Typed_SV_Message::type"); + return this->type_; +} + +template ACE_INLINE void +ACE_Typed_SV_Message::type (long t) +{ + ACE_TRACE ("ACE_Typed_SV_Message::type"); + this->type_ = t; +} + +template ACE_INLINE int +ACE_Typed_SV_Message::length (void) const +{ + ACE_TRACE ("ACE_Typed_SV_Message::length"); + return this->length_; +} + +template ACE_INLINE void +ACE_Typed_SV_Message::length (int len) +{ + ACE_TRACE ("ACE_Typed_SV_Message::length"); + this->length_ = len + (sizeof *this - (sizeof this->type_ + sizeof this->data_)); +} + +template ACE_INLINE int +ACE_Typed_SV_Message::max_size (void) const +{ + ACE_TRACE ("ACE_Typed_SV_Message::max_size"); + return this->max_; +} + +template ACE_INLINE void +ACE_Typed_SV_Message::max_size (int m) +{ + ACE_TRACE ("ACE_Typed_SV_Message::max_size"); + this->max_ = m + (sizeof *this - (sizeof this->type_ + sizeof this->data_)); +} + +template T & +ACE_Typed_SV_Message::data (void) +{ + ACE_TRACE ("ACE_Typed_SV_Message::data"); + return this->data_; +} + +template void +ACE_Typed_SV_Message::data (const T &d) +{ + ACE_TRACE ("ACE_Typed_SV_Message::data"); + this->data_ = d; +} + diff --git a/ace/Streams/Typed_SV_Message_Queue.cpp b/ace/Streams/Typed_SV_Message_Queue.cpp new file mode 100644 index 00000000000..1a0a3b99943 --- /dev/null +++ b/ace/Streams/Typed_SV_Message_Queue.cpp @@ -0,0 +1,53 @@ +// Typed_SV_Message_Queue.cpp +// $Id$ + +#ifndef ACE_TYPED_SV_MESSAGE_QUEUE_C +#define ACE_TYPED_SV_MESSAGE_QUEUE_C + +#include "ace/Typed_SV_Message.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Typed_SV_Message_Queue.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Typed_SV_Message_Queue.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Typed_SV_Message_Queue, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Typed_SV_Message_Queue) + +template void +ACE_Typed_SV_Message_Queue::dump (void) const +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::dump"); +} + +template +ACE_Typed_SV_Message_Queue::ACE_Typed_SV_Message_Queue (void) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::ACE_Typed_SV_Message_Queue"); +} + +template +ACE_Typed_SV_Message_Queue::ACE_Typed_SV_Message_Queue (key_t external_id, + int create, + int perms) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::ACE_Typed_SV_Message_Queue"); + if (this->open (external_id, create, perms) == -1) + ACE_ERROR ((LM_ERROR, + "ACE_Typed_SV_Message_Queue::ACE_Typed_SV_Message_Queue")); +} + +template +ACE_Typed_SV_Message_Queue::~ACE_Typed_SV_Message_Queue (void) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::~ACE_Typed_SV_Message_Queue"); +} + +#endif /* ACE_TYPED_SV_MESSAGE_QUEUE_C */ diff --git a/ace/Streams/Typed_SV_Message_Queue.h b/ace/Streams/Typed_SV_Message_Queue.h new file mode 100644 index 00000000000..20d2ec690c2 --- /dev/null +++ b/ace/Streams/Typed_SV_Message_Queue.h @@ -0,0 +1,86 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Typed_SV_Message_Queue.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_TYPED_MESSAGE_QUEUE_H +#define ACE_TYPED_MESSAGE_QUEUE_H +#include "ace/pre.h" + +#include "ace/SV_Message_Queue.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Typed_SV_Message.h" + +/** + * @class ACE_Typed_SV_Message_Queue + * + * @brief Defines the header file for the C++ wrapper facade for typed message queues. + */ +template +class ACE_Typed_SV_Message_Queue +{ +public: + enum + { + ACE_CREATE = IPC_CREAT, + ACE_OPEN = 0, + ACE_NOWAIT = IPC_NOWAIT + }; + + // = Initialization and termination operations. + ACE_Typed_SV_Message_Queue (void); + ACE_Typed_SV_Message_Queue (key_t external_id, + int create = ACE_OPEN, + int perms = ACE_DEFAULT_FILE_PERMS); + int open (key_t external_id, + int create = ACE_OPEN, + int perms = ACE_DEFAULT_FILE_PERMS); + int close (void); + int remove (void); + ~ACE_Typed_SV_Message_Queue (void); + + // = Send and recv methods. + int send (const ACE_Typed_SV_Message &mb, int mflags = 0); + int recv (ACE_Typed_SV_Message &mb, int mflags = 0); + + /// Return the id of the underlying . + int get_id (void) const; + + /// Control the underlying message queue. + int control (int option, void *arg = 0); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + ACE_SV_Message_Queue message_queue_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Typed_SV_Message_Queue.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Typed_SV_Message_Queue.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Typed_SV_Message_Queue.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TYPED_MESSAGE_QUEUE_H */ diff --git a/ace/Streams/Typed_SV_Message_Queue.i b/ace/Streams/Typed_SV_Message_Queue.i new file mode 100644 index 00000000000..afbea9c455d --- /dev/null +++ b/ace/Streams/Typed_SV_Message_Queue.i @@ -0,0 +1,77 @@ +/* -*- C++ -*- */ +// $Id$ + +// Typed_SV_Message_Queue.i + +#include "ace/SV_Message_Queue.h" + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::open (key_t external_id, + int create, + int perms) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::open"); + return this->message_queue_.open (external_id, create, perms); +} + +// What does it mean to close a message queue?! + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::close (void) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::close"); + return 1; +} + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::recv (ACE_Typed_SV_Message &mb, + int mflags) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::recv"); + + int length = this->message_queue_.recv (ACE_reinterpret_cast (ACE_SV_Message &, + mb), + mb.max_size (), + mb.type (), + mflags); + if (length != -1) + mb.length (length); + + return length; +} + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::send (const ACE_Typed_SV_Message &mb, + int mflags) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::send"); + return this->message_queue_. + send (ACE_reinterpret_cast (ACE_SV_Message &, + ACE_const_cast (ACE_Typed_SV_Message &, + mb)), + mb.length (), + mflags); +} + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::remove (void) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::remove"); + + return this->message_queue_.remove (); +} + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::control (int option, + void *arg) +{ + ACE_TRACE ("ACE_Typed_SV_Message_Queue::control"); + + return this->message_queue_.control (option, arg); +} + +template ACE_INLINE int +ACE_Typed_SV_Message_Queue::get_id (void) const +{ + return this->message_queue_.get_id (); +} diff --git a/ace/Streams/streams.h b/ace/Streams/streams.h new file mode 100644 index 00000000000..5ac89a44bcf --- /dev/null +++ b/ace/Streams/streams.h @@ -0,0 +1,149 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file streams.h + * + * $Id$ + * + * @author Irfan Pyarali + * + * This file contains the portability ugliness for the Standard C++ + * Library. As implementations of the "standard" emerge, this file + * will need to be updated. + * + * This files deals with the streams includes. + * + * + */ +//============================================================================= + + +#ifndef ACE_STREAMS_H +#define ACE_STREAMS_H +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once + +#endif /* ACE_LACKS_PRAGMA_ONCE */ +// Do this so the #pragma warning in the MSVC headers do not +// affect our #pragma warning settings +#if (_MSC_VER >= 1200) +#pragma warning(push) +#endif /* _MSC_VER >= 1200 */ + + +#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) + +# if defined (ACE_HAS_STANDARD_CPP_LIBRARY) && \ + (ACE_HAS_STANDARD_CPP_LIBRARY != 0) + +# if defined (_MSC_VER) +# pragma warning(disable: 4018 4114 4146 4245) +# pragma warning(disable: 4663 4664 4665 4511 4512) +# endif /* _MSC_VER */ + +# if defined (ACE_USES_OLD_IOSTREAMS) +# include /**/ +# include /**/ + // This has been commented as it is not needed and causes problems with Qt. + // (brunsch) But has been uncommented since it should be included. Qt + // probably should have some sort of macro that will prevent including this + // when it is used. +# include /**/ +# if defined (_MSC_VER) +# include /**/ +# else +# include /**/ +# endif /* _MSC_VER */ +# else +# include /**/ +# include /**/ +# include /**/ +# include /**/ +# include /**/ +# include /**/ +# include /**/ +# include /**/ +# endif /* ACE_USES_OLD_IOSTREAMS */ + +# if defined (ACE_USES_STD_NAMESPACE_FOR_STDCPP_LIB) && \ + (ACE_USES_STD_NAMESPACE_FOR_STDCPP_LIB != 0) + +# if !defined (ACE_USES_OLD_IOSTREAMS) + // Make these available in the global name space + using std::ios; + using std::streambuf; + using std::istream; + using std::ostream; + using std::iostream; + using std::filebuf; + using std::ifstream; + using std::ofstream; + using std::fstream; + + using std::istrstream; + using std::ostrstream; + + using std::cin; + using std::cout; + using std::cerr; + using std::clog; + + using std::endl; + using std::ends; + using std::flush; + + using std::ws; + + using std::resetiosflags; + using std::setfill; + using std::setiosflags; + using std::setprecision; + using std::setw; + + using std::dec; + using std::hex; + using std::oct; +# endif /* ! ACE_USES_OLD_IOSTREAMS */ + +# endif /* ACE_USES_STD_NAMESPACE_FOR_STDCPP_LIB */ + +# if defined (_MSC_VER) +# pragma warning(4: 4018 4114 4146 4245) +# pragma warning(4: 4663 4664 4665 4512 4511) +# endif /* _MSC_VER */ + +# else /* ! ACE_HAS_STANDARD_CPP_LIBRARY */ + +# include /**/ +# include /**/ +# include /**/ + +# if defined (ACE_WIN32) && !defined(__MINGW32__) +# if defined(_MSC_VER) // VSB +# include /**/ +# include /**/ +# include /**/ +# include /**/ +# endif /* _MSC_VER */ +# include /**/ // VSB +# else +# include /**/ +# endif /* ACE_WIN32 && !__MINGW32__ */ + +# endif /* ! ACE_HAS_STANDARD_CPP_LIBRARY */ + +#endif /* ! ACE_LACKS_IOSTREAM_TOTALLY */ + +// Do this so the #pragma warning in the MSVC headers do not +// affect our #pragma warning settings +#if (_MSC_VER >= 1200) +#pragma warning(pop) +#endif /* _MSC_VER >= 1200 */ + +#include "ace/post.h" +#endif /* ACE_STREAMS_H */ diff --git a/ace/Svcconf/DLL.cpp b/ace/Svcconf/DLL.cpp new file mode 100644 index 00000000000..622cbe081ab --- /dev/null +++ b/ace/Svcconf/DLL.cpp @@ -0,0 +1,188 @@ +// DLL.cpp +// $Id$ + +#include "ace/DLL.h" + +#include "ace/Log_Msg.h" +#include "ace/ACE.h" + +ACE_RCSID(ace, DLL, "$Id$") + +// Default constructor. Also, by default, the object will be closed +// before it is destroyed. + +ACE_DLL::ACE_DLL (int close_on_destruction) + : handle_ (ACE_SHLIB_INVALID_HANDLE), + close_on_destruction_ (close_on_destruction) +{ +} + +// If the library name and the opening mode are specified than on +// object creation the library is implicitly opened. + +ACE_DLL::ACE_DLL (const ACE_TCHAR *dll_name, + int open_mode, + int close_on_destruction) + : handle_ (ACE_OS::dlopen (dll_name, + open_mode)), + close_on_destruction_ (close_on_destruction) +{ + if (this->handle_ == ACE_SHLIB_INVALID_HANDLE) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%s\n"), + this->error ())); +} + +// The library is closed before the class gets destroyed depending on +// the close_on_destruction value specified which is stored in +// close_on_destruction_. + +ACE_DLL::~ACE_DLL (void) +{ + // CLose the library only if it hasn't been already. + this->close (); +} + +// This method opens the library based on the mode specified using the +// ACE_SHLIB_HANDLE which is obtained on making the ACE_OS::dlopen call. +// The default mode is: +// RTLD_LAZY Only references to data symbols are relocate when the +// object is first loaded. +// The other modes include: +// RTLD_NOW All necessary relocations are performed when the +// object is first loaded. +// RTLD_GLOBAL The object symbols are made available for the +// relocation processing of any other object. + +int +ACE_DLL::open (const ACE_TCHAR *dll_filename, + int open_mode, + int close_on_destruction) +{ + // This check is necessary as the library could be opened more than + // once without closing it which would cause handle memory leaks. + this->close (); + + // Reset the flag + this->close_on_destruction_ = close_on_destruction; + + // Find out where the library is + ACE_TCHAR dll_pathname[MAXPATHLEN + 1]; + + // Transform the pathname into the appropriate dynamic link library + // by searching the ACE_LD_SEARCH_PATH. + int result = ACE_Lib_Find::ldfind (dll_filename, + dll_pathname, + (sizeof dll_pathname / sizeof (ACE_TCHAR))); + // Check for errors + if (result != 0) + return result; + + // The ACE_SHLIB_HANDLE object is obtained. + this->handle_ = ACE_OS::dlopen (dll_pathname, + open_mode); + + if (this->handle_ == ACE_SHLIB_INVALID_HANDLE) + { +#if defined (AIX) + do + { + // AIX often puts the shared library file (most often named shr.o) + // inside an archive library. If this is an archive library + // name, then try appending [shr.o] and retry. + if (0 != ACE_OS_String::strstr (dll_pathname, ACE_LIB_TEXT (".a"))) + { + ACE_OS_String::strcat (dll_pathname, ACE_LIB_TEXT ("(shr.o)")); + open_mode |= RTLD_MEMBER; + this->handle_ = ACE_OS::dlopen (dll_pathname, open_mode); + if (this->handle_ != ACE_SHLIB_INVALID_HANDLE) + break; // end up returning 0 + } + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%s\n"), this->error ()), + -1); + } + while (0); +#else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%s\n"), this->error ()), + -1); +#endif /* AIX */ + } + + return 0; +} + +// The symbol refernce of the name specified is obtained. + +void * +ACE_DLL::symbol (const ACE_TCHAR *sym_name) +{ + return ACE_OS::dlsym (this->handle_, sym_name); +} + +// The library is closed using the ACE_SHLIB_HANDLE obejct. i.e. The +// shared object is now disassociated form the current process. + +int +ACE_DLL::close (void) +{ + int retval = 0; + + // The handle is checked to see whether the library is closed + // already and the flag is specified. If + // not, it is closed and the handle is made invalid to indicate that + // it's now closed. + if (this->close_on_destruction_ != 0 && + this->handle_ != ACE_SHLIB_INVALID_HANDLE) + { + retval = ACE_OS::dlclose (this->handle_); + } + + this->handle_ = ACE_SHLIB_INVALID_HANDLE; + return retval; +} + +// This method is used on error in an library operation. + +ACE_TCHAR * +ACE_DLL::error (void) +{ + return ACE_OS::dlerror (); +} + +// Return the handle to the user either temporarily or forever, thus +// orphaning it. If 0 means the user wants the handle forever and if 1 +// means the user temporarily wants to take the handle. + +ACE_SHLIB_HANDLE +ACE_DLL::get_handle (int become_owner) +{ + // Since the caller is becoming the owner of the handle we lose + // rights to close it on destruction. The new controller has to do + // it explicitly. + if (become_owner) + this->close_on_destruction_ = 0; + + // Return the handle requested by the user. + return this->handle_; +} + +// Set the handle for the DLL. By default, the object will be closed +// before it is destroyed. + +int +ACE_DLL::set_handle (ACE_SHLIB_HANDLE handle, + int close_on_destruction) +{ + // Close the handle in use before accepting the next one. + if (this->close () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%s\n"), this->error ()), + -1); + + this->handle_ = handle; + this->close_on_destruction_ = close_on_destruction; + + return 0; +} diff --git a/ace/Svcconf/DLL.h b/ace/Svcconf/DLL.h new file mode 100644 index 00000000000..2a870fa08c1 --- /dev/null +++ b/ace/Svcconf/DLL.h @@ -0,0 +1,115 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file DLL.h + * + * $Id$ + * + * @author Kirthika Parameswaran + */ +//============================================================================= + + +#ifndef ACE_DLL_H +#define ACE_DLL_H +#include "ace/pre.h" + +#include "ace/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_DLL + * + * @brief Provides an abstract interface for handling various DLL + * operations. + * + * This class is an wrapper over the various methods for utilizing + * a dynamically linked library (DLL), which is called a shared + * library on some platforms. Operations , , and + * have been implemented to help opening/closing and + * extracting symbol information from a DLL, respectively. + */ +class ACE_Export ACE_DLL +{ +public: + // = Initialization and termination methods. + + /// Default constructor. By default, the operation on the + /// object will be invoked before it is destroyed. + ACE_DLL (int close_on_destruction = 1); + + /** + * This constructor opens and dynamically links . The + * default mode is , which loads identifier symbols but + * not the symbols for functions, which are loaded dynamically + * on-demand. Other supported modes include: , which + * performs all necessary relocations when is first + * loaded and , which makes symbols available for + * relocation processing of any other DLLs. + */ + ACE_DLL (const ACE_TCHAR *dll_name, + int open_mode = ACE_DEFAULT_SHLIB_MODE, + int close_on_destruction = 1); + + /** + * This method opens and dynamically links . The default + * mode is , which loads identifier symbols but not the + * symbols for functions, which are loaded dynamically on-demand. + * Other supported modes include: , which performs all + * necessary relocations when is first loaded and + * , which makes symbols available for relocation + * processing of any other DLLs. Returns -1 on failure and 0 on + * success. + */ + int open (const ACE_TCHAR *dll_name, + int open_mode = ACE_DEFAULT_SHLIB_MODE, + int close_on_destruction = 1); + + /// Call to close the DLL object. + int close (void); + + /** + * Called when the DLL object is destroyed -- invokes if the + * flag is set in the constructor or + * method. + */ + ~ACE_DLL (void); + + /// If is in the symbol table of the DLL a pointer to + /// the is returned. Otherwise, returns 0. + void *symbol (const ACE_TCHAR *symbol_name); + + /// Returns a pointer to a string explaining why or + /// failed. + ACE_TCHAR *error (void); + + /** + * Return the handle to the caller. If is non-0 then + * caller assumes ownership of the handle and the object + * won't call when it goes out of scope, even if + * is set. + */ + ACE_SHLIB_HANDLE get_handle (int become_owner = 0); + + /// Set the handle for the DLL object. By default, the operation on the + /// object will be invoked before it is destroyed. + int set_handle (ACE_SHLIB_HANDLE handle, int close_on_destruction = 1); +private: + /// This is a handle to the DLL. + ACE_SHLIB_HANDLE handle_; + + /// This flag keeps track of whether we should close the handle + /// automatically when the destructor runs. + int close_on_destruction_; + + // = Disallow copying and assignment since we don't handle these. + ACE_UNIMPLEMENTED_FUNC (ACE_DLL (const ACE_DLL &)) + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_DLL &)) +}; + +#include "ace/post.h" +#endif /* ACE_DLL_H */ diff --git a/ace/Svcconf/Dynamic_Service.cpp b/ace/Svcconf/Dynamic_Service.cpp new file mode 100644 index 00000000000..ee40d45f112 --- /dev/null +++ b/ace/Svcconf/Dynamic_Service.cpp @@ -0,0 +1,19 @@ +// Dynamic_Service.cpp +// $Id$ + +#ifndef ACE_DYNAMIC_SERVICE_C +#define ACE_DYNAMIC_SERVICE_C + +#include "ace/Dynamic_Service.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if !defined (__ACE_INLINE__) +#include "ace/Dynamic_Service.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Dynamic_Service, "$Id$") + +#endif /* ACE_DYNAMIC_SERVICE_C */ diff --git a/ace/Svcconf/Dynamic_Service.h b/ace/Svcconf/Dynamic_Service.h new file mode 100644 index 00000000000..bd3a92c24af --- /dev/null +++ b/ace/Svcconf/Dynamic_Service.h @@ -0,0 +1,52 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Dynamic_Service.h + * + * $Id$ + * + * @author Prashant Jain + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_DYNAMIC_SERVICE_H +#define ACE_DYNAMIC_SERVICE_H +#include "ace/pre.h" + +#include "ace/OS.h" + +#include "ace/Dynamic_Service_Base.h" + +/** + * @class ACE_Dynamic_Service + * + * @brief Provides a general interface to retrieve arbitrary objects + * from the ACE service repository. + * + * Uses "name" for lookup in the ACE service repository. Obtains + * the object and returns it as the appropriate type. + */ +template +class ACE_Dynamic_Service : public ACE_Dynamic_Service_Base +{ +public: + /// Return instance using to search the Service_Repository. + static TYPE*instance (const ACE_TCHAR *name); +}; + +#if defined (__ACE_INLINE__) +#include "ace/Dynamic_Service.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +# include "ace/Dynamic_Service.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +# pragma implementation ("Dynamic_Service.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_DYNAMIC_SERVICE_H */ diff --git a/ace/Svcconf/Dynamic_Service.i b/ace/Svcconf/Dynamic_Service.i new file mode 100644 index 00000000000..2d0b32b056c --- /dev/null +++ b/ace/Svcconf/Dynamic_Service.i @@ -0,0 +1,8 @@ +/* -*- C++ -*- */ +// $Id$ + +template ACE_INLINE TYPE * +ACE_Dynamic_Service::instance (const ACE_TCHAR *name) +{ + return ACE_reinterpret_cast(TYPE*,ACE_Dynamic_Service_Base::instance (name)); +} diff --git a/ace/Svcconf/Dynamic_Service_Base.cpp b/ace/Svcconf/Dynamic_Service_Base.cpp new file mode 100644 index 00000000000..6bcc87ad1ec --- /dev/null +++ b/ace/Svcconf/Dynamic_Service_Base.cpp @@ -0,0 +1,39 @@ +// $Id$ + +#include "ace/Dynamic_Service_Base.h" +#include "ace/Service_Config.h" +#include "ace/Service_Repository.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(ace, Dynamic_Service_Base, "$Id$") + +void +ACE_Dynamic_Service_Base::dump (void) const +{ + ACE_TRACE ("ACE_Dynamic_Service_Base::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +// Get the instance using . + +void * +ACE_Dynamic_Service_Base::instance (const ACE_TCHAR *name) +{ + ACE_TRACE ("ACE_Dynamic_Service_Base::instance"); + const ACE_Service_Type *svc_rec; + + if (ACE_Service_Repository::instance ()->find (name, + &svc_rec) == -1) + return 0; + + const ACE_Service_Type_Impl *type = svc_rec->type (); + + if (type == 0) + return 0; + + void *obj = type->object (); + return obj; +} diff --git a/ace/Svcconf/Dynamic_Service_Base.h b/ace/Svcconf/Dynamic_Service_Base.h new file mode 100644 index 00000000000..15379c32e5b --- /dev/null +++ b/ace/Svcconf/Dynamic_Service_Base.h @@ -0,0 +1,40 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Dynamic_Service_Base.h + * + * $Id$ + * + * @author Prashant Jain + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_DYNAMIC_SERVICE_BASE_H +#define ACE_DYNAMIC_SERVICE_BASE_H +#include "ace/pre.h" + +#include "ace/OS.h" + +/** + * @class ACE_Dynamic_Service_Base + * + * @brief Base class for all ACE_Dynamic_Service instantiations. + * + * Factors out common code shared by all ACE_Dynamic_Service + * instantiations, this avoid code bloat. + */ +class ACE_Export ACE_Dynamic_Service_Base +{ +public: + /// Dump the current static of the object + void dump (void) const; + +protected: + /// Return instance using to search the Service_Repository. + static void* instance (const ACE_TCHAR *name); +}; + +#include "ace/post.h" +#endif /* ACE_DYNAMIC_SERVICE_BASE_H */ diff --git a/ace/Svcconf/Dynamic_Service_Base.i b/ace/Svcconf/Dynamic_Service_Base.i new file mode 100644 index 00000000000..6318deb79a0 --- /dev/null +++ b/ace/Svcconf/Dynamic_Service_Base.i @@ -0,0 +1,2 @@ +/* -*- C++ -*- */ +// $Id$ diff --git a/ace/Svcconf/Parse_Node.cpp b/ace/Svcconf/Parse_Node.cpp new file mode 100644 index 00000000000..899398bae90 --- /dev/null +++ b/ace/Svcconf/Parse_Node.cpp @@ -0,0 +1,652 @@ +// $Id$ + +#include "ace/Service_Config.h" +#include "ace/Service_Repository.h" +#include "ace/Task.h" +#include "ace/Parse_Node.h" + +// Provide the class hierarchy that defines the parse tree of Service +// Nodes. + +#if !defined (__ACE_INLINE__) +#include "ace/Parse_Node.i" +#endif /* ____ */ + +ACE_RCSID(ace, Parse_Node, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Stream_Node) + +void +ACE_Stream_Node::dump (void) const +{ + ACE_TRACE ("ACE_Stream_Node::dump"); +} + +void +ACE_Stream_Node::apply (void) +{ + ACE_TRACE ("ACE_Stream_Node::apply"); + + if (ACE_Service_Config::initialize (this->node_->record (), + this->node_->parameters ()) == -1) + ace_yyerrno++; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did stream on %s, error = %d\n"), + this->node_->name (), + ace_yyerrno)); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Parse_Node) + +void +ACE_Parse_Node::dump (void) const +{ + ACE_TRACE ("ACE_Parse_Node::dump"); +} + +const ACE_TCHAR * +ACE_Parse_Node::name (void) const +{ + ACE_TRACE ("ACE_Parse_Node::name"); + return this->name_; +} + +ACE_Parse_Node * +ACE_Parse_Node::link (void) const +{ + ACE_TRACE ("ACE_Parse_Node::link"); + return this->next_; +} + +void +ACE_Parse_Node::link (ACE_Parse_Node *n) +{ + ACE_TRACE ("ACE_Parse_Node::link"); + this->next_ = n; +} + +ACE_Stream_Node::ACE_Stream_Node (const ACE_Static_Node *str_ops, + const ACE_Parse_Node *str_mods) +#if defined (ACE_HAS_BROKEN_CONDITIONAL_STRING_CASTS) + : ACE_Parse_Node (str_ops == 0 ? ACE_static_cast (ACE_TCHAR *, + ACE_LIB_TEXT ("")) + : ACE_static_cast (ACE_TCHAR *, + str_ops->name ())), +#else + : ACE_Parse_Node ((str_ops == 0 ? ACE_LIB_TEXT ("") : str_ops->name ())), +#endif /* defined (ACE_HAS_BROKEN_CONDITIONAL_STRING_CASTS) */ + node_ (str_ops), + mods_ (str_mods) +{ + ACE_TRACE ("ACE_Stream_Node::ACE_Stream_Node"); +} + + +ACE_Stream_Node::~ACE_Stream_Node (void) +{ + ACE_TRACE ("ACE_Stream_Node::~ACE_Stream_Node"); + ACE_Static_Node *n = ACE_const_cast (ACE_Static_Node *, + this->node_); + delete n; + ACE_Parse_Node *m = ACE_const_cast (ACE_Parse_Node *, + this->mods_); + delete m; +} + +ACE_Parse_Node::ACE_Parse_Node (void) + : name_ (0), + next_ (0) +{ + ACE_TRACE ("ACE_Parse_Node::ACE_Parse_Node"); +} + + +ACE_Parse_Node::ACE_Parse_Node (const ACE_TCHAR *nm) + : name_ (ACE::strnew (nm)), + next_ (0) +{ + ACE_TRACE ("ACE_Parse_Node::ACE_Parse_Node"); +} + +void +ACE_Parse_Node::print (void) const +{ + ACE_TRACE ("ACE_Parse_Node::print"); + + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("svc = %s\n"), + this->name ())); + + if (this->next_) + this->next_->print (); +} + + +ACE_Parse_Node::~ACE_Parse_Node (void) +{ + ACE_TRACE ("ACE_Parse_Node::~ACE_Parse_Node"); + delete[] ACE_const_cast (ACE_TCHAR*, this->name_); + delete this->next_; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Suspend_Node) + +void +ACE_Suspend_Node::dump (void) const +{ + ACE_TRACE ("ACE_Suspend_Node::dump"); +} + +ACE_Suspend_Node::ACE_Suspend_Node (const ACE_TCHAR *name) + : ACE_Parse_Node (name) +{ + ACE_TRACE ("ACE_Suspend_Node::ACE_Suspend_Node"); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Resume_Node) + +void +ACE_Resume_Node::dump (void) const +{ + ACE_TRACE ("ACE_Resume_Node::dump"); +} + +ACE_Resume_Node::ACE_Resume_Node (const ACE_TCHAR *name) + : ACE_Parse_Node (name) +{ + ACE_TRACE ("ACE_Resume_Node::ACE_Resume_Node"); +} + +void +ACE_Suspend_Node::apply (void) +{ + ACE_TRACE ("ACE_Suspend_Node::apply"); + + if (ACE_Service_Config::suspend (this->name ()) == -1) + ace_yyerrno++; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did suspend on %s, error = %d\n"), + this->name (), + ace_yyerrno)); +} + +void +ACE_Resume_Node::apply (void) +{ + ACE_TRACE ("ACE_Resume_Node::apply"); + if (ACE_Service_Config::resume (this->name ()) == -1) + ace_yyerrno++; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did resume on %s, error = %d\n"), + this->name (), + ace_yyerrno)); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Remove_Node) + +void +ACE_Remove_Node::dump (void) const +{ + ACE_TRACE ("ACE_Remove_Node::dump"); +} + +ACE_Remove_Node::ACE_Remove_Node (const ACE_TCHAR *name) + : ACE_Parse_Node (name) +{ + ACE_TRACE ("ACE_Remove_Node::ACE_Remove_Node"); +} + +void +ACE_Remove_Node::apply (void) +{ + ACE_TRACE ("ACE_Remove_Node::apply"); + if (ACE_Service_Config::remove (this->name ()) == -1) + ace_yyerrno++; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did remove on %s, error = %d\n"), + this->name (), + ace_yyerrno)); +} + +ACE_Dynamic_Node::ACE_Dynamic_Node (const ACE_Service_Type *sr, + ACE_TCHAR *parms) + : ACE_Static_Node (sr->name (), parms), + record_ (sr) +{ + ACE_TRACE ("ACE_Dynamic_Node::ACE_Dynamic_Node"); +} + +const ACE_Service_Type * +ACE_Dynamic_Node::record (void) const +{ + ACE_TRACE ("ACE_Dynamic_Node::record"); + return this->record_; +} + +void +ACE_Dynamic_Node::apply (void) +{ + ACE_TRACE ("ACE_Dynamic_Node::apply"); + + if (ACE_Service_Config::initialize (this->record (), + this->parameters ()) == -1) + ace_yyerrno++; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did dynamic on %s, error = %d\n"), + this->name (), + ace_yyerrno)); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Dynamic_Node) + +void +ACE_Dynamic_Node::dump (void) const +{ + ACE_TRACE ("ACE_Dynamic_Node::dump"); +} + +ACE_Dynamic_Node::~ACE_Dynamic_Node (void) +{ + ACE_TRACE ("ACE_Dynamic_Node::~ACE_Dynamic_Node"); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Static_Node) + +void +ACE_Static_Node::dump (void) const +{ + ACE_TRACE ("ACE_Static_Node::dump"); +} + +ACE_Static_Node::ACE_Static_Node (const ACE_TCHAR *nm, + ACE_TCHAR *params) + : ACE_Parse_Node (nm), + parameters_ (ACE::strnew (params)) +{ + ACE_TRACE ("ACE_Static_Node::ACE_Static_Node"); +} + +const ACE_Service_Type * +ACE_Static_Node::record (void) const +{ + ACE_TRACE ("ACE_Static_Node::record"); + ACE_Service_Type *sr; + + if (ACE_Service_Repository::instance()->find + (this->name (), + (const ACE_Service_Type **) &sr) == -1) + return 0; + else + return sr; +} + +ACE_TCHAR * +ACE_Static_Node::parameters (void) const +{ + ACE_TRACE ("ACE_Static_Node::parameters"); + return this->parameters_; +} + +void +ACE_Static_Node::apply (void) +{ + ACE_TRACE ("ACE_Static_Node::apply"); + if (ACE_Service_Config::initialize (this->name (), + this->parameters ()) == -1) + ace_yyerrno++; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did static on %s, error = %d\n"), + this->name (), + ace_yyerrno)); +} + +ACE_Static_Node::~ACE_Static_Node (void) +{ + ACE_TRACE ("ACE_Static_Node::~ACE_Static_Node"); + delete[] this->parameters_; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Location_Node) + +void +ACE_Location_Node::dump (void) const +{ + ACE_TRACE ("ACE_Location_Node::dump"); +} + +ACE_Location_Node::ACE_Location_Node (void) + : pathname_ (0), + symbol_ (0) +{ + ACE_TRACE ("ACE_Location_Node::ACE_Location_Node"); +} + +ACE_Location_Node::~ACE_Location_Node (void) +{ + ACE_TRACE ("ACE_Location_Node::~ACE_Location_Node"); +} + +ACE_SHLIB_HANDLE +ACE_Location_Node::handle (void) +{ + ACE_TRACE ("ACE_Location_Node::handle"); + return this->dll_.get_handle (1); // Caller now owns the handle +} + +const ACE_TCHAR * +ACE_Location_Node::pathname (void) const +{ + ACE_TRACE ("ACE_Location_Node::pathname"); + return this->pathname_; +} + +void +ACE_Location_Node::pathname (const ACE_TCHAR *p) +{ + ACE_TRACE ("ACE_Location_Node::pathname"); + this->pathname_ = p; +} + +void +ACE_Location_Node::set_symbol (void *s) +{ + ACE_TRACE ("ACE_Location_Node::set_symbol"); + this->symbol_ = s; +} + +int +ACE_Location_Node::dispose (void) const +{ + ACE_TRACE ("ACE_Location_Node::dispose"); + return this->must_delete_; +} + +int +ACE_Location_Node::open_dll (void) +{ + ACE_TRACE ("ACE_Location_Node::open_dll"); + + if (-1 == this->dll_.open (this->pathname ())) + { + ace_yyerrno++; + + ACE_TCHAR *errmsg = this->dll_.error (); + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_DLL::open failed for %s: %s\n"), + this->pathname (), + errmsg ? errmsg : ACE_LIB_TEXT ("no error reported"))); + return -1; + } + + return 0; + +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Object_Node) + +void +ACE_Object_Node::dump (void) const +{ + ACE_TRACE ("ACE_Object_Node::dump"); +} + +ACE_Object_Node::ACE_Object_Node (const ACE_TCHAR *path, + const ACE_TCHAR *obj_name) + : object_name_ (obj_name ? ACE_Lib_Find::ldname (obj_name) : 0) +{ + ACE_TRACE ("ACE_Object_Node::ACE_Object_Node"); + this->pathname (ACE::strnew (path)); + this->must_delete_ = 0; +} + +void * +ACE_Object_Node::symbol (ACE_Service_Object_Exterminator *) +{ + ACE_TRACE ("ACE_Object_Node::symbol"); + if (this->open_dll () == 0) + { + ACE_TCHAR *object_name = ACE_const_cast (ACE_TCHAR *, this->object_name_); + + this->symbol_ = this->dll_.symbol (object_name); + if (this->symbol_ == 0) + { + ace_yyerrno++; + + ACE_TCHAR *errmsg = this->dll_.error (); + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_DLL::symbol failed for object %s: %s\n"), + object_name, + errmsg ? errmsg : ACE_LIB_TEXT ("no error reported"))); + return 0; + } + + return this->symbol_; + } + + return 0; +} + +ACE_Object_Node::~ACE_Object_Node (void) +{ + ACE_TRACE ("ACE_Object_Node::~ACE_Object_Node"); + delete[] ACE_const_cast (ACE_TCHAR *, this->object_name_); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Function_Node) + +void +ACE_Function_Node::dump (void) const +{ + ACE_TRACE ("ACE_Function_Node::dump"); +} + +ACE_Function_Node::ACE_Function_Node (const ACE_TCHAR *path, + const ACE_TCHAR *func_name) + : function_name_ (func_name ? ACE_Lib_Find::ldname (func_name) : 0) +{ + ACE_TRACE ("ACE_Function_Node::ACE_Function_Node"); + this->pathname (ACE::strnew (path)); + this->must_delete_ = 1; +} + +void * +ACE_Function_Node::symbol (ACE_Service_Object_Exterminator *gobbler) +{ + ACE_TRACE ("ACE_Function_Node::symbol"); + if (this->open_dll () == 0) + { + void *(*func) (ACE_Service_Object_Exterminator *) = 0; + this->symbol_ = 0; + + // Locate the factory function in the shared + // object. + + ACE_TCHAR *function_name = ACE_const_cast (ACE_TCHAR *, + this->function_name_); + + // According to the new ANSI C++ specification, casting a void* + // pointer to a function pointer is not allowed. However, + // casting a void* pointer to an integer type that is large + // enough to hold the pointer value is legal. I (Nanbor) chose + // to cast the return value to long since it should be large + // enough to hold the void* pointer's value on most platforms. + // I am not sure if casting a long value to a function pointer + // is legal or not (can't find a good explanation in spec) but + // SunC++, egcs, and KAI compilers, all of which are pretty + // close to (or, at least claim to conform with) the standard + // did not complain about this as an illegal pointer conversion. + long temp_ptr = + ACE_reinterpret_cast(long, this->dll_.symbol (function_name)); + func = ACE_reinterpret_cast(void *(*)(ACE_Service_Object_Exterminator *), + temp_ptr); + + if (func == 0) + { + ace_yyerrno++; + + if (this->symbol_ == 0) + { + ace_yyerrno++; + + ACE_TCHAR *errmsg = this->dll_.error (); + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_DLL::symbol failed for function %s: %s\n"), + function_name, + errmsg ? errmsg : + ACE_LIB_TEXT ("no error reported"))); + return 0; + } + } + // Invoke the factory function and record it's return value. + this->symbol_ = (*func) (gobbler); + + if (this->symbol_ == 0) + { + ace_yyerrno++; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + this->function_name_), + 0); + } + } + return this->symbol_; +} + +ACE_Function_Node::~ACE_Function_Node (void) +{ + ACE_TRACE ("ACE_Function_Node::~ACE_Function_Node"); + delete[] ACE_const_cast (ACE_TCHAR *, function_name_); + delete[] ACE_const_cast (ACE_TCHAR *, pathname_); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Dummy_Node) + +void +ACE_Dummy_Node::dump (void) const +{ + ACE_TRACE ("ACE_Dummy_Node::dump"); +} + +ACE_Dummy_Node::ACE_Dummy_Node (const ACE_Static_Node *static_node, + const ACE_Parse_Node *str_mods) + : ACE_Parse_Node (static_node->name ()), + node_ (static_node), + mods_ (str_mods) +{ + ACE_TRACE ("ACE_Dummy_Node::ACE_Dummy_Node"); +} + +void +ACE_Dummy_Node::apply (void) +{ + ACE_TRACE ("ACE_Dummy_Node::apply"); + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("did operations on stream %s, error = %d\n"), + this->name (), + ace_yyerrno)); +} + +ACE_Dummy_Node::~ACE_Dummy_Node (void) +{ + ACE_TRACE ("ACE_Dummy_Node::~ACE_Dummy_Node"); + ACE_Static_Node *n = ACE_const_cast (ACE_Static_Node *, + this->node_); + delete n; + ACE_Parse_Node *m = ACE_const_cast (ACE_Parse_Node *, + this->mods_); + delete m; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Static_Function_Node) + +void +ACE_Static_Function_Node::dump (void) const +{ + ACE_TRACE ("ACE_Static_Function_Node::dump"); +} + +ACE_Static_Function_Node::ACE_Static_Function_Node (const ACE_TCHAR *func_name) + : function_name_ (ACE::strnew (func_name)) +{ + ACE_TRACE ("ACE_Static_Function_Node::ACE_Static_Function_Node"); + this->must_delete_ = 1; +} + +void * +ACE_Static_Function_Node::symbol (ACE_Service_Object_Exterminator *gobbler) +{ + ACE_TRACE ("ACE_Static_Function_Node::symbol"); + + void *(*func)(ACE_Service_Object_Exterminator *) = 0; + this->symbol_ = 0; + + // Locate the factory function in the statically + // linked svcs. + + ACE_Static_Svc_Descriptor **ssdp = 0; + ACE_STATIC_SVCS &svcs = *ACE_Service_Config::static_svcs (); + ACE_TCHAR *function_name = ACE_const_cast (ACE_TCHAR *, this->function_name_); + + for (ACE_STATIC_SVCS_ITERATOR iter (svcs); + iter.next (ssdp) != 0; + iter.advance ()) + { + ACE_Static_Svc_Descriptor *ssd = *ssdp; + if (ACE_OS::strcmp (ssd->name_, + function_name) == 0) + func = (void *(*)(ACE_Service_Object_Exterminator*)) ssd->alloc_; + } + + if (func == 0) + { + ace_yyerrno++; + + if (this->symbol_ == 0) + { + ace_yyerrno++; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("no static service registered for function %s\n"), + function_name), + 0); + } + } + + // Invoke the factory function and record it's return value. + this->symbol_ = (*func) (gobbler); + + if (this->symbol_ == 0) + { + ace_yyerrno++; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + this->function_name_), + 0); + } + + return this->symbol_; +} + +ACE_Static_Function_Node::~ACE_Static_Function_Node (void) +{ + ACE_TRACE ("ACE_Static_Function_Node::~ACE_Static_Function_Node"); + delete[] ACE_const_cast (ACE_TCHAR *, this->function_name_); +} + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Svcconf/Parse_Node.h b/ace/Svcconf/Parse_Node.h new file mode 100644 index 00000000000..6df9def1915 --- /dev/null +++ b/ace/Svcconf/Parse_Node.h @@ -0,0 +1,343 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Parse_Node.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_PARSE_NODE_H +#define ACE_PARSE_NODE_H +#include "ace/pre.h" + +#include "ace/Service_Types.h" +#include "ace/DLL.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_Parse_Node + * + * @brief Provide the base of the object hierarchy that defines the parse + * tree of Service Nodes. + */ +class ACE_Export ACE_Parse_Node +{ +public: + ACE_Parse_Node (void); + ACE_EXPLICIT ACE_Parse_Node (const ACE_TCHAR *name); + virtual ~ACE_Parse_Node (void); + + ACE_Parse_Node *link (void) const; + void link (ACE_Parse_Node *); + virtual void apply (void) = 0; + + const ACE_TCHAR *name (void) const; + void print (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + const ACE_TCHAR *name_; + ACE_Parse_Node *next_; +}; + +/** + * @class ACE_Suspend_Node + * + * @brief Suspend a Service Node. + */ +class ACE_Export ACE_Suspend_Node : public ACE_Parse_Node +{ +public: + ACE_Suspend_Node (const ACE_TCHAR *name); + ~ACE_Suspend_Node (void); + + virtual void apply (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Resume_Node + * + * @brief Resume a Service Node. + */ +class ACE_Export ACE_Resume_Node : public ACE_Parse_Node +{ +public: + ACE_Resume_Node (const ACE_TCHAR *name); + ~ACE_Resume_Node (void); + + virtual void apply (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Remove_Node + * + * @brief Remove a Service Node. + */ +class ACE_Export ACE_Remove_Node : public ACE_Parse_Node +{ +public: + ACE_Remove_Node (const ACE_TCHAR *name); + ~ACE_Remove_Node (void); + + virtual void apply (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Static_Node + * + * @brief Handle a statically linked node. + */ +class ACE_Export ACE_Static_Node : public ACE_Parse_Node +{ +public: + ACE_Static_Node (const ACE_TCHAR *name, ACE_TCHAR *params = 0); + virtual ~ACE_Static_Node (void); + + virtual void apply (void); + virtual const ACE_Service_Type *record (void) const; + ACE_TCHAR *parameters (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// "Command-line" parameters. + ACE_TCHAR *parameters_; +}; + +/** + * @class ACE_Dynamic_Node + * + * @brief Handle a dynamically linked node. + */ +class ACE_Export ACE_Dynamic_Node : public ACE_Static_Node +{ +public: + ACE_Dynamic_Node (const ACE_Service_Type *, ACE_TCHAR *params); + virtual ~ACE_Dynamic_Node (void); + + virtual const ACE_Service_Type *record (void) const; + virtual void apply (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Pointer to a descriptor that describes this node. + const ACE_Service_Type *record_; +}; + +/** + * @class ACE_Stream_Node + * + * @brief Handle a Stream. + */ +class ACE_Export ACE_Stream_Node : public ACE_Parse_Node +{ +public: + ACE_Stream_Node (const ACE_Static_Node *, const ACE_Parse_Node *); + virtual ~ACE_Stream_Node (void); + + virtual void apply (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Linked list of modules that are part of the stream. + const ACE_Static_Node *node_; + const ACE_Parse_Node *mods_; +}; + +/** + * @class ACE_Location_Node + * + * @brief Keep track of where a shared library is located. + */ +class ACE_Export ACE_Location_Node +{ +public: + ACE_Location_Node (void); + virtual void *symbol (ACE_Service_Object_Exterminator * = 0) = 0; + virtual void set_symbol (void *h); + ACE_SHLIB_HANDLE handle (void); + const ACE_TCHAR *pathname (void) const; + void pathname (const ACE_TCHAR *h); + int dispose (void) const; + + virtual ~ACE_Location_Node (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + int open_dll (void); + + /// Pathname to the shared library we are working on. + const ACE_TCHAR *pathname_; + + /** + * Flag indicating whether the Service_Object generated by this + * Location Node should be deleted or not + * (ACE_Service_Type::DELETE_OBJ.) + */ + int must_delete_; + + /// The open shared library. + ACE_DLL dll_; + + /// Symbol that we've obtained from the shared library. + void *symbol_; +}; + +/** + * @class ACE_Object_Node + * + * @brief Keeps track of the symbol name for a shared object. + */ +class ACE_Export ACE_Object_Node : public ACE_Location_Node +{ +public: + ACE_Object_Node (const ACE_TCHAR *pathname, const ACE_TCHAR *obj_name); + virtual void *symbol (ACE_Service_Object_Exterminator * = 0); + virtual ~ACE_Object_Node (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Name of the object that we're parsing. + const ACE_TCHAR *object_name_; +}; + +/** + * @class ACE_Function_Node + * + * @brief Keeps track of the symbol name of for a shared function. + */ +class ACE_Export ACE_Function_Node : public ACE_Location_Node +{ +public: + ACE_Function_Node (const ACE_TCHAR *pathname, const ACE_TCHAR *func_name); + virtual void *symbol (ACE_Service_Object_Exterminator *gobbler = 0); + virtual ~ACE_Function_Node (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Name of the function that we're parsing. + const ACE_TCHAR *function_name_; +}; + +/** + * @class ACE_Dummy_Node + * + * @brief I forget why this is here... ;-) + */ +class ACE_Export ACE_Dummy_Node : public ACE_Parse_Node +{ +public: + ACE_Dummy_Node (const ACE_Static_Node *, const ACE_Parse_Node *); + ~ACE_Dummy_Node (void); + virtual void apply (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Linked list of modules that we're dealing with. + const ACE_Static_Node *node_; + const ACE_Parse_Node *mods_; +}; + +/** + * @class ACE_Static_Function_Node + * + * @brief Keeps track of the symbol name for a function that is not + * linked in from a DLL, but is statically linked with the + * application. + */ +class ACE_Export ACE_Static_Function_Node : public ACE_Location_Node +{ +public: + ACE_EXPLICIT ACE_Static_Function_Node (const ACE_TCHAR *func_name); + virtual void *symbol (ACE_Service_Object_Exterminator * = 0); + virtual ~ACE_Static_Function_Node (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Name of the function that we're parsing. + const ACE_TCHAR *function_name_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Parse_Node.i" +#endif /* __ACE_INLINE__ */ + +// Keeps track of the number of errors encountered so far. +extern int ace_yyerrno; + +// Global variable used to communicate between the parser and the main +// program. +extern ACE_Service_Config *ace_this_svc; + +#include "ace/post.h" +#endif /* ACE_PARSE_NODE_H */ diff --git a/ace/Svcconf/Parse_Node.i b/ace/Svcconf/Parse_Node.i new file mode 100644 index 00000000000..0728118ea80 --- /dev/null +++ b/ace/Svcconf/Parse_Node.i @@ -0,0 +1,19 @@ +/* -*- C++ -*- */ +// $Id$ + +// Parse_Node.i + +ACE_INLINE +ACE_Suspend_Node::~ACE_Suspend_Node (void) +{ +} + +ACE_INLINE +ACE_Resume_Node::~ACE_Resume_Node (void) +{ +} + +ACE_INLINE +ACE_Remove_Node::~ACE_Remove_Node (void) +{ +} diff --git a/ace/Svcconf/Service_Config.cpp b/ace/Svcconf/Service_Config.cpp new file mode 100644 index 00000000000..05768a716ef --- /dev/null +++ b/ace/Svcconf/Service_Config.cpp @@ -0,0 +1,884 @@ +// $Id$ + +#include "ace/Svc_Conf.h" +#include "ace/Get_Opt.h" +#include "ace/ARGV.h" +#include "ace/Malloc.h" +#include "ace/Service_Manager.h" +#include "ace/Service_Repository.h" +#include "ace/Service_Types.h" +#include "ace/Containers.h" +#include "ace/Auto_Ptr.h" +#include "ace/Reactor.h" +#include "ace/Proactor.h" +#include "ace/Thread_Manager.h" + +#include "ace/Service_Config.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Service_Config.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (ace, + Service_Config, + "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Config) + +void +ACE_Service_Config::dump (void) const +{ + ACE_TRACE ("ACE_Service_Config::dump"); +} + +// All the factory functions that allocate default statically linked +// services should be placed below. + +// Allocate a Service Manager. + +ACE_FACTORY_DEFINE (ACE, ACE_Service_Manager) + +// ---------------------------------------- + +// Set the signal handler to point to the handle_signal() function. +ACE_Sig_Adapter *ACE_Service_Config::signal_handler_ = 0; + +// Trigger a reconfiguration. +sig_atomic_t ACE_Service_Config::reconfig_occurred_ = 0; + + // = Set by command-line options. +int ACE_Service_Config::be_a_daemon_ = 0; +int ACE_Service_Config::no_static_svcs_ = 1; + +// Number of the signal used to trigger reconfiguration. +int ACE_Service_Config::signum_ = SIGHUP; + +// Indicates where to write the logging output. This is typically +// either a STREAM pipe or a socket address. +const ACE_TCHAR *ACE_Service_Config::logger_key_ = ACE_DEFAULT_LOGGER_KEY; + +// The ACE_Service_Manager static service object is now defined by the +// ACE_Object_Manager, in Object_Manager.cpp. + +// Are we initialized already? +int ACE_Service_Config::is_initialized_ = 0; + +// List of statically configured services. + +ACE_STATIC_SVCS *ACE_Service_Config::static_svcs_ = 0; +ACE_SVC_QUEUE *ACE_Service_Config::svc_queue_ = 0; +ACE_SVC_QUEUE *ACE_Service_Config::svc_conf_file_queue_ = 0; + +ACE_STATIC_SVCS * +ACE_Service_Config::static_svcs (void) +{ + if (ACE_Service_Config::static_svcs_ == 0) + ACE_NEW_RETURN (ACE_Service_Config::static_svcs_, + ACE_STATIC_SVCS, + 0); + return ACE_Service_Config::static_svcs_; +} + +ACE_Allocator * +ACE_Service_Config::alloc (void) +{ + ACE_TRACE ("ACE_Service_Config::allocator"); + return ACE_Allocator::instance (); +} + +ACE_Allocator * +ACE_Service_Config::alloc (ACE_Allocator *r) +{ + ACE_TRACE ("ACE_Service_Config::allocator"); + return ACE_Allocator::instance (r); +} + +ACE_Reactor * +ACE_Service_Config::reactor (void) +{ + ACE_TRACE ("ACE_Service_Config::reactor"); + return ACE_Reactor::instance (); +} + +ACE_Reactor * +ACE_Service_Config::reactor (ACE_Reactor *r) +{ + ACE_TRACE ("ACE_Service_Config::reactor"); + return ACE_Reactor::instance (r); +} + +ACE_Service_Repository * +ACE_Service_Config::svc_rep () +{ + ACE_TRACE ("ACE_Service_Config::svc_rep"); + return ACE_Service_Repository::instance (); +} + +ACE_Service_Repository * +ACE_Service_Config::svc_rep (ACE_Service_Repository *s) +{ + ACE_TRACE ("ACE_Service_Config::svc_rep"); + return ACE_Service_Repository::instance (s); +} + +ACE_Thread_Manager * +ACE_Service_Config::thr_mgr (void) +{ + ACE_TRACE ("ACE_Service_Config::thr_mgr"); + +#if defined (ACE_THREAD_MANAGER_LACKS_STATICS) + return ACE_THREAD_MANAGER_SINGLETON::instance (); +#else /* ! ACE_THREAD_MANAGER_LACKS_STATICS */ + return ACE_Thread_Manager::instance (); +#endif /* ACE_THREAD_MANAGER_LACKS_STATICS */ +} + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) +ACE_Thread_Manager * +ACE_Service_Config::thr_mgr (ACE_Thread_Manager *tm) +{ + ACE_TRACE ("ACE_Service_Config::thr_mgr"); + return ACE_Thread_Manager::instance (tm); +} +#endif /* ! ACE_THREAD_MANAGER_LACKS_STATICS */ + +// Totally remove from the daemon by removing it from the +// ACE_Reactor, and unlinking it if necessary. + +int +ACE_Service_Config::remove (const ACE_TCHAR svc_name[]) +{ + ACE_TRACE ("ACE_Service_Config::remove"); + return ACE_Service_Repository::instance ()->remove (svc_name); +} + +// Suspend . Note that this will not unlink the service +// from the daemon if it was dynamically linked, it will mark it as +// being suspended in the Service Repository and call the +// member function on the appropriate . A service +// can be resumed later on by calling the method... + +int +ACE_Service_Config::suspend (const ACE_TCHAR svc_name[]) +{ + ACE_TRACE ("ACE_Service_Config::suspend"); + return ACE_Service_Repository::instance ()->suspend (svc_name); +} + +// Resume a SVC_NAME that was previously suspended or has not yet +// been resumed (e.g., a static service). + +int +ACE_Service_Config::resume (const ACE_TCHAR svc_name[]) +{ + ACE_TRACE ("ACE_Service_Config::resume"); + return ACE_Service_Repository::instance ()->resume (svc_name); +} + +// Initialize the Service Repository. Note that this *must* be +// performed in the constructor (rather than ) since otherwise +// the repository will not be properly initialized to allow static +// configuration of services... + +ACE_Service_Config::ACE_Service_Config (int ignore_static_svcs, + size_t size, + int signum) +{ + ACE_TRACE ("ACE_Service_Config::ACE_Service_Config"); + ACE_Service_Config::no_static_svcs_ = ignore_static_svcs; + ACE_Service_Config::signum_ = signum; + + // Initialize the Service Repository. + ACE_Service_Repository::instance (size); + + // Initialize the ACE_Reactor (the ACE_Reactor should be the same + // size as the ACE_Service_Repository). + ACE_Reactor::instance (); +} + +int +ACE_Service_Config::init_svc_conf_file_queue (void) +{ + if (ACE_Service_Config::svc_conf_file_queue_ == 0) + ACE_NEW_RETURN (ACE_Service_Config::svc_conf_file_queue_, + ACE_SVC_QUEUE, + -1); + return 0; +} + +// Handle the command-line options intended for the +// ACE_Service_Config. + +int +ACE_Service_Config::parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_TRACE ("ACE_Service_Config::parse_args"); + ACE_Get_Opt getopt (argc, + argv, + ACE_LIB_TEXT ("bdf:k:nys:S:"), + 1); // Start at argv[1]. + + if (ACE_Service_Config::init_svc_conf_file_queue () == -1) + return -1; + + for (int c; (c = getopt ()) != -1; ) + switch (c) + { + case 'b': + ACE_Service_Config::be_a_daemon_ = 1; + break; + case 'd': + ACE::debug (1); + break; + case 'f': + if (ACE_Service_Config::svc_conf_file_queue_->enqueue_tail + (ACE_TString (getopt.opt_arg ())) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + "enqueue_tail"), + -1); + break; + case 'k': + ACE_Service_Config::logger_key_ = getopt.opt_arg (); + break; + case 'n': + ACE_Service_Config::no_static_svcs_ = 1; + break; + case 'y': + ACE_Service_Config::no_static_svcs_ = 0; + break; + case 's': + { + // There's no point in dealing with this on NT since it + // doesn't really support signals very well... +#if !defined (ACE_LACKS_UNIX_SIGNALS) + ACE_Service_Config::signum_ = + ACE_OS::atoi (getopt.opt_arg ()); + + if (ACE_Reactor::instance ()->register_handler + (ACE_Service_Config::signum_, + ACE_Service_Config::signal_handler_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("cannot obtain signal handler\n")), + -1); +#endif /* ACE_LACKS_UNIX_SIGNALS */ + break; + } + case 'S': + if (ACE_Service_Config::svc_queue_ == 0) + ACE_NEW_RETURN (ACE_Service_Config::svc_queue_, + ACE_SVC_QUEUE, + -1); + if (ACE_Service_Config::svc_queue_->enqueue_tail + (ACE_TString (getopt.opt_arg ())) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + "enqueue_tail"), + -1); + break; + default: + if (ACE::debug () > 0) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("%c is not a ACE_Service_Config option\n"), + c)); + } + + return 0; +} + +// Initialize and activate a statically linked service. + +int +ACE_Service_Config::initialize (const ACE_TCHAR svc_name[], + ACE_TCHAR *parameters) +{ + ACE_TRACE ("ACE_Service_Config::initialize"); + ACE_ARGV args (parameters); + ACE_Service_Type *srp = 0; + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("opening static service %s\n"), + svc_name)); + + if (ACE_Service_Repository::instance ()->find + (svc_name, + (const ACE_Service_Type **) &srp) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%s not found\n"), + svc_name), + -1); + else if (srp->type ()->init (args.argc (), + args.argv ()) == -1) + { + // Remove this entry. + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("static initialization failed, %p\n"), + svc_name)); + ACE_Service_Repository::instance ()->remove (svc_name); + return -1; + } + else + { + srp->active (1); + return 0; + } +} + +// Dynamically link the shared object file and retrieve a pointer to +// the designated shared object in this file. + +int +ACE_Service_Config::initialize (const ACE_Service_Type *sr, + ACE_TCHAR parameters[]) +{ + ACE_TRACE ("ACE_Service_Config::initialize"); + ACE_ARGV args (parameters); + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("opening dynamic service %s\n"), + sr->name ())); + + if (sr->type ()->init (args.argc (), + args.argv ()) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("dynamic initialization failed for %s\n"), + sr->name ())); + return -1; + } + else + { + if (ACE_Service_Repository::instance ()->insert (sr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("insertion failed, %p\n"), + sr->name ()), + -1); + return 0; + } +} + +int +ACE_Service_Config::process_directives_i (ACE_Svc_Conf_Param *param) +{ + // AC 970827 Skip the heap check because yacc allocates a buffer + // here which will be reported as a memory leak for some reason. + ACE_NO_HEAP_CHECK + + // The fact that these are global variables means that we really + // can't track the number of errors in multiple threads + // simultaneously. + ace_yyerrno = 0; + ace_yylineno = 1; + + ace_yyparse (param); + + if (param->yyerrno > 0) + { + // This is a hack, better errors should be provided... + errno = EINVAL; + return param->yyerrno; + } + else + return 0; +} + +int +ACE_Service_Config::process_directive (const ACE_TCHAR directive[]) +{ + ACE_TRACE ("ACE_Service_Config::process_directive"); + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("Service_Config::process_directive - %s\n"), + directive)); + + ACE_UNUSED_ARG (directive); + + ACE_Svc_Conf_Param d (directive); + + int result = ACE_Service_Config::process_directives_i (&d); + + return result; +} + +// Process service configuration requests as indicated in the queue of +// svc.conf files. + +int +ACE_Service_Config::process_directives (void) +{ + ACE_TRACE ("ACE_Service_Config::process_directives"); + + int result = 0; + + if (ACE_Service_Config::svc_conf_file_queue_ != 0) + { + ACE_TString *sptr = 0; + ACE_SVC_QUEUE &queue = *ACE_Service_Config::svc_conf_file_queue_; + + // Iterate through all the svc.conf files. + for (ACE_SVC_QUEUE_ITERATOR iter (queue); + iter.next (sptr) != 0; + iter.advance ()) + { + FILE *fp = ACE_OS::fopen (sptr->fast_rep (), + ACE_LIB_TEXT ("r")); + if (fp == 0) + { + // Invalid svc.conf file. We'll report it here and + // break out of the method. + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("%p\n"), + sptr->fast_rep ())); + errno = ENOENT; + result = -1; + break; + } + else + { + ACE_Svc_Conf_Param f (fp); + + // Keep track of the number of errors. + result += ACE_Service_Config::process_directives_i (&f); + } + ACE_OS::fclose (fp); + } + } + + return result; +} + +int +ACE_Service_Config::process_commandline_directives (void) +{ + int result = 0; + + if (ACE_Service_Config::svc_queue_ != 0) + { + ACE_TString *sptr = 0; + ACE_SVC_QUEUE &queue = *ACE_Service_Config::svc_queue_; + + for (ACE_SVC_QUEUE_ITERATOR iter (queue); + iter.next (sptr) != 0; + iter.advance ()) + { + // Process just a single directive. + if (ACE_Service_Config::process_directive (sptr->fast_rep ()) != 0) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("process_directive"))); + result = -1; + } + } + + delete ACE_Service_Config::svc_queue_; + ACE_Service_Config::svc_queue_ = 0; + } + + return result; +} + +int +ACE_Service_Config::process_directive (const ACE_Static_Svc_Descriptor &ssd, + int force_replace) +{ + if (!force_replace) + { + if (ACE_Service_Repository::instance ()->find (ssd.name_, + 0, 0) >= 0) + { + // The service is already there, just return + return 0; + } + } + + ACE_Service_Object_Exterminator gobbler; + void *sym = (ssd.alloc_)(&gobbler); + + ACE_Service_Type_Impl *stp = + ace_create_service_type (ssd.name_, + ssd.type_, + sym, + ssd.flags_, + gobbler); + if (stp == 0) + return 0; + + + ACE_Service_Type *service_type; + ACE_NEW_RETURN (service_type, + ACE_Service_Type (ssd.name_, + stp, + 0, + ssd.active_), + -1); + + return ACE_Service_Repository::instance ()->insert (service_type); +} + +// Add the default statically-linked services to the Service +// Repository. + +int +ACE_Service_Config::load_static_svcs (void) +{ + ACE_TRACE ("ACE_Service_Config::load_static_svcs"); + + ACE_Static_Svc_Descriptor **ssdp = 0; + ACE_STATIC_SVCS &svcs = *ACE_Service_Config::static_svcs (); + + for (ACE_STATIC_SVCS_ITERATOR iter (svcs); + iter.next (ssdp) != 0; + iter.advance ()) + { + ACE_Static_Svc_Descriptor *ssd = *ssdp; + + if (ACE_Service_Config::process_directive (*ssd, 1) == -1) + return -1; + } + return 0; +} + +// Performs an open without parsing command-line arguments. + +int +ACE_Service_Config::open_i (const ACE_TCHAR program_name[], + const ACE_TCHAR *logger_key, + int ignore_default_svc_conf_file, + int ignore_debug_flag) +{ + int result = 0; + ACE_TRACE ("ACE_Service_Config::open_i"); + ACE_Log_Msg *log_msg = ACE_LOG_MSG; + + // Record the current log setting upon entering this thread. + u_long old_process_mask = log_msg->priority_mask + (ACE_Log_Msg::PROCESS); + u_long old_thread_mask = log_msg->priority_mask + (ACE_Log_Msg::THREAD); + + if (ACE_Service_Config::is_initialized_ != 0) + // Guard against reentrant processing! + return 0; + else + ACE_Service_Config::is_initialized_++; + + if (ACE_Service_Config::init_svc_conf_file_queue () == -1) + return -1; + else if (!ignore_default_svc_conf_file + && ACE_Service_Config::svc_conf_file_queue_->is_empty () + // Load the default "svc.conf" entry here if there weren't + // overriding -f arguments in . + && ACE_Service_Config::svc_conf_file_queue_->enqueue_tail + (ACE_TString (ACE_DEFAULT_SVC_CONF)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + "enqueue_tail"), + -1); + + if (ignore_debug_flag == 0) + { + // If -d was included as a startup parameter, the user wants debug + // information printed during service initialization. + if (ACE::debug ()) + ACE_Log_Msg::enable_debug_messages (); + else + // The user has requested no debugging info. + ACE_Log_Msg::disable_debug_messages (); + } + + // Become a daemon before doing anything else. + if (ACE_Service_Config::be_a_daemon_) + ACE_Service_Config::start_daemon (); + + u_long flags = log_msg->flags (); + + if (flags == 0) + // Only use STDERR if the caller hasn't already set the flags. + flags = (u_long) ACE_Log_Msg::STDERR; + + const ACE_TCHAR *key = logger_key; + + if (key == 0 || ACE_OS::strcmp (key, ACE_DEFAULT_LOGGER_KEY) == 0) + // Only use the static if the caller doesn't + // override it in the parameter list or if the key supplied is + // equal to the default static logger key. + key = ACE_Service_Config::logger_key_; + else + ACE_SET_BITS (flags, ACE_Log_Msg::LOGGER); + + if (log_msg->open (program_name, + flags, + key) == -1) + result = -1; + else + { + if (ACE::debug ()) + ACE_DEBUG ((LM_STARTUP, + ACE_LIB_TEXT ("starting up daemon %n\n"))); + + // Initialize the Service Repository (this will still work if + // user forgets to define an object of type ACE_Service_Config). + ACE_Service_Repository::instance (ACE_Service_Config::MAX_SERVICES); + + // Initialize the ACE_Reactor (the ACE_Reactor should be the + // same size as the ACE_Service_Repository). + ACE_Reactor::instance (); + + // There's no point in dealing with this on NT since it doesn't + // really support signals very well... +#if !defined (ACE_LACKS_UNIX_SIGNALS) + // @@ This really ought to be a Singleton. + if (ACE_Reactor::instance ()->register_handler + (ACE_Service_Config::signum_, + ACE_Service_Config::signal_handler_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("can't register signal handler\n"))); +#endif /* ACE_LACKS_UNIX_SIGNALS */ + + // See if we need to load the static services. + if (ACE_Service_Config::no_static_svcs_ == 0 + && ACE_Service_Config::load_static_svcs () == -1) + result = -1; + else + { + if (ACE_Service_Config::process_commandline_directives () == -1) + result = -1; + else + result = ACE_Service_Config::process_directives (); + } + } + + { + // Make sure to save/restore errno properly. + ACE_Errno_Guard error (errno); + + if (ignore_debug_flag == 0) + { + // Reset debugging back to the way it was when we came into + // into . + log_msg->priority_mask (old_process_mask, ACE_Log_Msg::PROCESS); + log_msg->priority_mask (old_thread_mask, ACE_Log_Msg::THREAD); + } + } + + return result; +} + +ACE_Service_Config::ACE_Service_Config (const ACE_TCHAR program_name[], + const ACE_TCHAR *logger_key) +{ + ACE_TRACE ("ACE_Service_Config::ACE_Service_Config"); + + if (this->open (program_name, + logger_key) == -1 + && errno != ENOENT) + // Only print out an error if it wasn't the svc.conf file that was + // missing. + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + program_name)); +} + +// Signal handling API to trigger dynamic reconfiguration. + +void +ACE_Service_Config::handle_signal (int sig, + siginfo_t *, + ucontext_t *) +{ +#if defined (ACE_NDEBUG) + ACE_UNUSED_ARG (sig); +#else /* ! ACE_NDEBUG */ + ACE_ASSERT (ACE_Service_Config::signum_ == sig); +#endif /* ! ACE_NDEBUG */ + + ACE_Service_Config::reconfig_occurred_ = 1; +} + +// Trigger the reconfiguration process. + +void +ACE_Service_Config::reconfigure (void) +{ + ACE_TRACE ("ACE_Service_Config::reconfigure"); + + ACE_Service_Config::reconfig_occurred_ = 0; + + if (ACE::debug ()) + { +#if !defined (ACE_NLOGGING) + time_t t = ACE_OS::time (0); +#endif /* ! ACE_NLOGGING */ + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("beginning reconfiguration at %s"), + ACE_OS::ctime (&t))); + } + if (ACE_Service_Config::process_directives () == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("process_directives"))); +} + +// Run the event loop until the +// method returns -1 or the method +// is invoked. + +int +ACE_Service_Config::run_reactor_event_loop (void) +{ + ACE_TRACE ("ACE_Service_Config::run_reactor_event_loop"); + + return ACE_Reactor::run_event_loop (); +} + +// Run the event loop until the method +// returns -1, the method is invoked, or the +// expires. + +int +ACE_Service_Config::run_reactor_event_loop (ACE_Time_Value &tv) +{ + ACE_TRACE ("ACE_Service_Config::run_reactor_event_loop"); + + return ACE_Reactor::run_event_loop (tv); +} + +/* static */ +int +ACE_Service_Config::end_reactor_event_loop (void) +{ + ACE_TRACE ("ACE_Service_Config::end_reactor_event_loop"); + return ACE_Reactor::end_event_loop (); +} + +/* static */ +int +ACE_Service_Config::reactor_event_loop_done (void) +{ + ACE_TRACE ("ACE_Service_Config::reactor_event_loop_done"); + return ACE_Reactor::event_loop_done (); +} + +// Tidy up and perform last rites on a terminating ACE_Service_Config. +int +ACE_Service_Config::close (void) +{ + ACE_TRACE ("ACE_Service_Config::close"); + + ACE_Service_Config::is_initialized_--; + if (ACE_Service_Config::is_initialized_ > 0) + return 0; + + // Delete the service repository. All the objects inside the + // service repository should already have been finalized. + ACE_Service_Config::close_svcs (); + + // Delete the list fo svc.conf files + delete ACE_Service_Config::svc_conf_file_queue_; + ACE_Service_Config::svc_conf_file_queue_ = 0; + + // Delete the dynamically allocated static_svcs instance. + delete ACE_Service_Config::static_svcs_; + ACE_Service_Config::static_svcs_ = 0; + + return 0; +} + +int +ACE_Service_Config::close_svcs (void) +{ + ACE_TRACE ("ACE_Service_Config::close_svcs"); + + ACE_Service_Repository::close_singleton (); + + return 0; +} + +int +ACE_Service_Config::fini_svcs (void) +{ + ACE_TRACE ("ACE_Service_Config::fini_svcs"); + + // Clear the LM_DEBUG bit from log messages if appropriate + if (ACE::debug ()) + ACE_Log_Msg::disable_debug_messages (); + + int result = 0; + if (ACE_Service_Repository::instance () != 0) + result = ACE_Service_Repository::instance ()->fini (); + + // Since the fini() method of the objects inside the service + // repository may reference the ACE singletons, they must be + // destroyed after the objects have been finalized. + ACE_Service_Config::close_singletons (); + + if (ACE::debug ()) + ACE_Log_Msg::enable_debug_messages (); + + return result; +} + +int +ACE_Service_Config::close_singletons (void) +{ + ACE_TRACE ("ACE_Service_Config::close_singletons"); + + ACE_Reactor::close_singleton (); + +#if (((defined (ACE_HAS_WINNT)) && (ACE_HAS_WINNT == 1)) || (defined (ACE_HAS_AIO_CALLS))) + ACE_Proactor::close_singleton (); +#endif /* !ACE_HAS_WINCE */ + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) + ACE_Thread_Manager::close_singleton (); +#endif /* ! ACE_THREAD_MANAGER_LACKS_STATICS */ + + return 0; +} + +// Perform user-specified close activities and remove dynamic memory. + +ACE_Service_Config::~ACE_Service_Config (void) +{ + ACE_TRACE ("ACE_Service_Config::~ACE_Service_Config"); +} + +// ************************************************************ + +/* static */ +int +ACE_Service_Config::reconfig_occurred (void) +{ + ACE_TRACE ("ACE_Service_Config::reconfig_occurred"); + return ACE_Service_Config::reconfig_occurred_ != 0; +} + +void +ACE_Service_Config::reconfig_occurred (int config_occurred) +{ + ACE_TRACE ("ACE_Service_Config::reconfig_occurred"); + ACE_Service_Config::reconfig_occurred_ = config_occurred; +} + +// Become a daemon (i.e., run as a "background" process). + +int +ACE_Service_Config::start_daemon (void) +{ + ACE_TRACE ("ACE_Service_Config::start_daemon"); + return ACE::daemonize (); +} + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Array; +template class ACE_Array_Base; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Array +#pragma instantiate ACE_Array_Base +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Svcconf/Service_Config.h b/ace/Svcconf/Service_Config.h new file mode 100644 index 00000000000..ffe4c4fd85e --- /dev/null +++ b/ace/Svcconf/Service_Config.h @@ -0,0 +1,516 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Service_Config.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SERVICE_CONFIG_H +#define ACE_SERVICE_CONFIG_H +#include "ace/pre.h" + +#include "ace/Svcconf/Service_Object.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/IPC/Signal.h" +#include "ace/Utils/Unbounded_Queue.h" +#include "ace/Utils/Unbounded_Set.h" +#include "ace/Utils/SString.h" + +// Forward decl. +class ACE_Service_Repository; +class ACE_Service_Type; +class ACE_Allocator; +class ACE_Reactor; +class ACE_Thread_Manager; +class ACE_Svc_Conf_Param; + +extern "C" +{ + typedef ACE_Service_Object *(*ACE_SERVICE_ALLOCATOR) (ACE_Service_Object_Exterminator *); +} + +/** + * @class ACE_Static_Svc_Descriptor + * + * @brief Holds the information necessary to describe a statically linked + * Svc. + */ +class ACE_Static_Svc_Descriptor +{ +public: + /// Name of the service. + const ACE_TCHAR *name_; + + /// Type of service. + int type_; + + /// Factory function that allocates the service. + ACE_SERVICE_ALLOCATOR alloc_; + + /// Bitmask flags indicating how the framework should delete memory. + u_int flags_; + + /// Flag indicating whether the service starts out active. + int active_; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +public: + /// Compare two service descriptors for equality. + int operator== (ACE_Static_Svc_Descriptor &) const; + + /// Compare two service descriptors for inequality. + int operator!= (ACE_Static_Svc_Descriptor &) const; +}; + +// Maintain a set of the statically linked service descriptors. +typedef ACE_Unbounded_Set + ACE_STATIC_SVCS; +typedef ACE_Unbounded_Set_Iterator + ACE_STATIC_SVCS_ITERATOR; + +// Maintain a queue of services to be configured from the +// command-line. +typedef ACE_Unbounded_Queue + ACE_SVC_QUEUE; +typedef ACE_Unbounded_Queue_Iterator + ACE_SVC_QUEUE_ITERATOR; + +#define ACE_Component_Config ACE_Service_Config +/** + * @class ACE_Service_Config + * + * @brief Supplies common server operations for dynamic and static + * configuration of services. + * + * The uses the Monostate pattern. Therefore, + * you can only have one of these instantiated per-process. + * NOTE: the signal_handler_ static member is allocated by the + * . The constructor + * uses signal_handler_. Therefore, if the program has any + * static objects, there might be + * initialization order problems. They can be minimized, but + * not eliminated, by _not_ #defining + * . + */ +class ACE_Export ACE_Service_Config +{ +public: + enum + { + MAX_SERVICES = ACE_DEFAULT_SERVICE_REPOSITORY_SIZE + }; + + // = Initialization and termination methods. + + /// Initialize the Service Repository. + ACE_Service_Config (int ignore_static_svcs = 1, + size_t size = ACE_Service_Config::MAX_SERVICES, + int signum = SIGHUP); + + /** + * Performs an open without parsing command-line arguments. The + * indicates where to write the logging output, which + * is typically either a STREAM pipe or a socket address. + */ + ACE_Service_Config (const ACE_TCHAR program_name[], + const ACE_TCHAR *logger_key = ACE_DEFAULT_LOGGER_KEY); + + /** + * Performs an open without parsing command-line arguments. The + * indicates where to write the logging output, which + * is typically either a STREAM pipe or a socket address. If + * is non-0 then the "svc.conf" file + * will be ignored. If is non-0 then the + * application is responsible for setting the + * appropriately. Returns number of + * errors that occurred on failure and 0 otherwise. + */ + static int open_i (const ACE_TCHAR program_name[], + const ACE_TCHAR *logger_key = ACE_DEFAULT_LOGGER_KEY, + int ignore_default_svc_conf_file = 0, + int ignore_debug_flag = 0); + + /** + * Performs an open without parsing command-line arguments. The + * indicates where to write the logging output, which + * is typically either a STREAM pipe or a socket address. If + * is 1 then static services are not loaded, + * otherwise, they are loaded. If is + * non-0 then the configuration file will be ignored. + * Returns zero upon success, -1 if the file is not found or cannot + * be opened (errno is set accordingly), otherwise returns the + * number of errors encountered loading the services in the + * specified svc.conf configuration file. If is + * non-0 then the application is responsible for setting the + * appropriately. + */ + static int open (const ACE_TCHAR program_name[], + const ACE_TCHAR *logger_key = ACE_DEFAULT_LOGGER_KEY, + int ignore_static_svcs = 1, + int ignore_default_svc_conf_file = 0, + int ignore_debug_flag = 0); + + /** + * This is the primary entry point into the ACE_Service_Config (the + * constructor just handles simple initializations). It parses + * arguments passed in from and parameters. The + * arguments that are valid in a call to this method include: + * + * - '-b' Option to indicate that we should be a daemon + * - '-d' Turn on debugging mode + * - '-f' Option to read in the list of svc.conf file names + * - '-k' Option to read a wide string where in the logger output can + * be written + * - '-y' Option required to use statically linked services. + * A static service repostory will be constructed if the flag + * is used. Use this flag to override the default + * flag at run-time. + * - '-n' Option to avoid using any statically linked services, which + * eliminates the need to construct the static service repository. + * - '-S' Option to read in the list of services on the command-line + * Please observe the difference between options '-f' that looks + * for a list of files and here a list of services. + * + * Returns number of errors that occurred on failure and 0 + * otherwise. + * + * The indicates where to write the logging output, + * which is typically either a STREAM pipe or a socket address. If + * is 1 then static services are not loaded, + * otherwise, they are loaded. If is + * non-0 then the configuration file will be ignored. + * Returns zero upon success, -1 if the file is not found or cannot + * be opened (errno is set accordingly), otherwise returns the + * number of errors encountered loading the services in the + * specified svc.conf configuration file. If is + * non-0 then the application is responsible for setting the + * appropriately. + */ + static int open (int argc, + ACE_TCHAR *argv[], + const ACE_TCHAR *logger_key = ACE_DEFAULT_LOGGER_KEY, + int ignore_static_svcs = 1, + int ignore_default_svc_conf = 0, + int ignore_debug_flag = 0); + + /// Perform user-specified close activities and remove dynamic + /// memory. + virtual ~ACE_Service_Config (void); + + /// Tidy up and perform last rites when ACE_Service_Config is shut + /// down. This method calls . Returns 0. + static int close (void); + + /// Perform user-specified close hooks and possibly delete all of the + /// configured services in the . + static int fini_svcs (void); + + /** + * Perform user-specified close hooks on all of the configured + * services in the , then delete the + * itself. Returns 0. + */ + static int close_svcs (void); + + /** + * Delete the dynamically allocated Singletons (i.e., the , + * , , and . + * Returns 0. + */ + static int close_singletons (void); + + // = Reactor event loop management methods. + /** + * Run the event loop until the method + * returns -1 or the method is invoked. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static int run_reactor_event_loop (void); + + /** + * Run the event loop until the method + * returns -1, the method is invoked, or the + * expires. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * instead. + */ + static int run_reactor_event_loop (ACE_Time_Value &tv); + + /** + * Instruct the to terminate its event loop and + * notifies the so that it can wake up + * and close down gracefully. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static int end_reactor_event_loop (void); + + /** + * Report if the Reactor's event loop is finished. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static int reactor_event_loop_done (void); + + /// True if reconfiguration occurred. + static int reconfig_occurred (void); + + /// Indicate that reconfiguration occurred. + static void reconfig_occurred (int); + + /// Perform the reconfiguration process. + static void reconfigure (void); + + // = The following methods are static in order to enforce Singleton + // semantics for the Reactor, Service_Repository, Thread_Manager, + // and Acceptor/Connector Strategy factory. Other portions of the + // system may need to access them at some point or another... + + // = Accessors and mutators for process-wide Singletons. + + /// Returns a pointer to the list of statically linked services. + static ACE_STATIC_SVCS *static_svcs (void); + + /** + * Get pointer to a process-wide . + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Reactor *reactor (void); + + /** + * Set pointer to a process-wide and return existing + * pointer. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Reactor *reactor (ACE_Reactor *); + + /** + * Get pointer to a process-wide . + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Service_Repository *svc_rep (void); + + /** + * Set pointer to a process-wide and return + * existing pointer. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Service_Repository *svc_rep (ACE_Service_Repository *); + + /** + * Get pointer to a process-wide . + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Thread_Manager *thr_mgr (void); + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) + /** + * Set pointer to a process-wide and return + * existing pointer. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use ACE_Thread_Manager::instance() instead. + */ + static ACE_Thread_Manager *thr_mgr (ACE_Thread_Manager *); +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + + /** + * Get pointer to a default . + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Allocator *alloc (void); + + /** + * Set pointer to a process-wide and return existing + * pointer. + * DO NOT USE THIS METHOD. It may be unsupported in future releases. + * Use instead. + */ + static ACE_Allocator *alloc (ACE_Allocator *); + + // = Utility methods. + /// Dynamically link the shared object file and retrieve a pointer to + /// the designated shared object in this file. + static int initialize (const ACE_Service_Type *, + ACE_TCHAR parameters[]); + + /// Initialize and activate a statically service. + static int initialize (const ACE_TCHAR svc_name[], + ACE_TCHAR parameters[]); + + /// Resume a that was previously suspended or has not yet + /// been resumed (e.g., a static service). + static int resume (const ACE_TCHAR svc_name[]); + + /** + * Suspend . Note that this will not unlink the service + * from the daemon if it was dynamically linked, it will mark it as + * being suspended in the Service Repository and call the + * member function on the appropriate . A + * service can be resumed later on by calling the member + * function... + */ + static int suspend (const ACE_TCHAR svc_name[]); + + /// Totally remove from the daemon by removing it + /// from the ACE_Reactor, and unlinking it if necessary. + static int remove (const ACE_TCHAR svc_name[]); + +#if defined (ACE_HAS_WINCE) + // We must provide these function to bridge the Svc_Conf parser + // with ACE. + static int initialize (const ACE_Service_Type *, char parameters[]); + static int initialize (const char svc_name[], char parameters[]); + static int resume (const char svc_name[]); + static int suspend (const char svc_name[]); + static int remove (const char svc_name[]); +#endif /* ACE_HAS_WINCE */ + + /// Dump the state of an object. + void dump (void) const; + + /// Set the signal_handler;for internal use by ACE_Object_Manager only. + static ACE_INLINE void signal_handler (ACE_Sig_Adapter *); + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + /// Process one service configuration , which is passed as + /// a string. Returns the number of errors that occurred. + static int process_directive (const ACE_TCHAR directive[]); + + /// Process one static service definition. + /** + * Load a new static service into the ACE_Service_Repository. + * + * @param ssd Service descriptor, see the document of + * ACE_Static_Svc_Descriptor for more details. + * + * @param force_replace If set the new service descriptor replaces + * any previous instance in the ACE_Service_Repository. + * + * @return Returns -1 if the service cannot be 'loaded'. + */ + static int process_directive (const ACE_Static_Svc_Descriptor &ssd, + int force_replace = 0); + + /** + * Process (or re-process) service configuration requests that are + * provided in the svc.conf file(s). Returns the number of errors + * that occurred. + */ + static int process_directives (void); + + /// Handles signals to trigger reconfigurations. + static void handle_signal (int sig, siginfo_t *, ucontext_t *); + + /** + * Handle the command-line options intended for the + * . Note that is assumed to be the + * program name. + * The arguments that are valid in a call to this method are + * - '-b' Option to indicate that we should be a daemon + * - '-d' Turn on debugging mode + * - '-f' Option to read in the list of svc.conf file names + * - '-k' Option to read a wide string where in the logger output can + * be written + * - '-y' Turn on the flag for a repository of statically + * linked services + * - '-n' Need not have a repository of statically linked services + * - '-S' Option to read in the list of services on the command-line + * Please observe the difference between options '-f' that looks + * for a list of files and here a list of services. + */ + static int parse_args (int, ACE_TCHAR *argv[]); +protected: + /// Process service configuration requests that were provided on the + /// command-line. Returns the number of errors that occurred. + static int process_commandline_directives (void); + + /// This is the implementation function that process_directives() + /// and process_directive() both call. Returns the number of errors + /// that occurred. + static int process_directives_i (ACE_Svc_Conf_Param *param); + + /// Become a daemon. + static int start_daemon (void); + + /// Add the default statically-linked services to the + /// . + static int load_static_svcs (void); + +private: + /// Indicates where to write the logging output. This is typically + /// either a STREAM pipe or a socket address. + static const ACE_TCHAR *logger_key_; + + /// Singleton repository of statically linked services. + static ACE_STATIC_SVCS *static_svcs_; + + /// Queue of services specified on the command-line. + static ACE_SVC_QUEUE *svc_queue_; + + /// Queue of svc.conf files specified on the command-line. + /// @@ This should probably be made to handle unicode filenames... + static ACE_SVC_QUEUE *svc_conf_file_queue_; + + /// Initialize the if necessary. + static int init_svc_conf_file_queue (void); + + /// True if reconfiguration occurred. + static sig_atomic_t reconfig_occurred_; + + // = Set by command-line options. + /// Shall we become a daemon process? + static int be_a_daemon_; + + /// Should we avoid loading the static services? + static int no_static_svcs_; + + /// Number of the signal used to trigger reconfiguration. + static int signum_; + + /// Handles the reconfiguration signals. + static ACE_Sig_Adapter *signal_handler_; + + /** + * Keep track of whether the is already + * initialized. If so, we can't allow to be called since + * it's not reentrant. This variable is incremented by the + * method and decremented by the + * method. + */ + static int is_initialized_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Svcconf/Service_Config.i" +#endif /* __ACE_INLINE__ */ + +// These must go here to avoid circular includes... (only left here +// for to not break applications which rely on this - no real need any +// longer) +#include "ace/Demux/Reactor.h" +#include "ace/Svcconf/Svc_Conf_Tokens.h" +#include "ace/post.h" +#endif /* ACE_SERVICE_CONFIG_H */ diff --git a/ace/Svcconf/Service_Config.i b/ace/Svcconf/Service_Config.i new file mode 100644 index 00000000000..42577cb1e04 --- /dev/null +++ b/ace/Svcconf/Service_Config.i @@ -0,0 +1,101 @@ +// -*- C++ -*- +// +// $Id$ + + +// This is the primary entry point into the ACE_Service_Config (the +// constructor just handles simple initializations). + +ACE_INLINE int +ACE_Service_Config::open (const ACE_TCHAR program_name[], + const ACE_TCHAR *logger_key, + int ignore_static_svcs, + int ignore_default_svc_conf, + int ignore_debug_flag) +{ + ACE_TRACE ("ACE_Service_Config::open"); + ACE_Service_Config::no_static_svcs_ = ignore_static_svcs; + + return ACE_Service_Config::open_i (program_name, + logger_key, + ignore_default_svc_conf, + ignore_debug_flag); +} + +ACE_INLINE int +ACE_Service_Config::open (int argc, + ACE_TCHAR *argv[], + const ACE_TCHAR *logger_key, + int ignore_static_svcs, + int ignore_default_svc_conf, + int ignore_debug_flag) +{ + ACE_TRACE ("ACE_Service_Config::open"); + ACE_Service_Config::no_static_svcs_ = ignore_static_svcs; + + if (ACE_Service_Config::parse_args (argc, + argv) == -1) + return -1; + else + return ACE_Service_Config::open_i (argv[0], + logger_key, + ignore_default_svc_conf, + ignore_debug_flag); +} + +// Compare two service descriptors for equality. + +ACE_INLINE int +ACE_Static_Svc_Descriptor::operator== (ACE_Static_Svc_Descriptor &d) const +{ + return ACE_OS::strcmp (name_, d.name_) == 0; +} + +// Compare two service descriptors for inequality. + +ACE_INLINE int +ACE_Static_Svc_Descriptor::operator!= (ACE_Static_Svc_Descriptor &d) const +{ + return !(*this == d); +} + +ACE_INLINE void +ACE_Service_Config::signal_handler (ACE_Sig_Adapter *signal_handler) +{ + signal_handler_ = signal_handler; +} + +#if defined (ACE_HAS_WINCE) && !defined (ACE_USES_WCHAR) + // We must provide these function to bridge Svc_Conf parser with ACE. + +ACE_INLINE int +ACE_Service_Config::initialize (const ACE_Service_Type *sp, char parameters[]) +{ + return ACE_Service_Config::initialize (sp, ACE_TEXT_CHAR_TO_TCHAR (parameters)); +} + +ACE_INLINE int +ACE_Service_Config::initialize (const char svc_name[], char parameters[]) +{ + return ACE_Service_Config::initialize (ACE_TEXT_CHAR_TO_TCHAR (svc_name), + ACE_TEXT_CHAR_TO_TCHAR (parameters)); +} + +ACE_INLINE int +ACE_Service_Config::resume (const char svc_name[]) +{ + return ACE_Service_Config::resume (ACE_TEXT_CHAR_TO_TCHAR (svc_name)); +} + +ACE_INLINE int +ACE_Service_Config::suspend (const char svc_name[]) +{ + return ACE_Service_Config::suspend (ACE_TEXT_CHAR_TO_TCHAR (svc_name)); +} + +ACE_INLINE int +ACE_Service_Config::remove (const char svc_name[]) +{ + return ACE_Service_Config::remove (ACE_TEXT_CHAR_TO_TCHAR (svc_name)); +} +#endif /* ACE_HAS_WINCE && !ACE_USES_WCHAR */ diff --git a/ace/Svcconf/Service_Manager.cpp b/ace/Svcconf/Service_Manager.cpp new file mode 100644 index 00000000000..8d94f95bb70 --- /dev/null +++ b/ace/Svcconf/Service_Manager.cpp @@ -0,0 +1,367 @@ +// $Id$ + +#include "ace/Get_Opt.h" +#include "ace/Service_Repository.h" +#include "ace/Service_Config.h" +#include "ace/Service_Manager.h" +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Service_Manager.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Service_Manager, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Manager) + +void +ACE_Service_Manager::dump (void) const +{ + ACE_TRACE ("ACE_Service_Manager::dump"); +} + +// Static variables. + +u_short ACE_Service_Manager::DEFAULT_PORT_ = 10000; + +ACE_Service_Manager::ACE_Service_Manager (void) + : debug_ (0), + signum_ (SIGHUP) +{ + ACE_TRACE ("ACE_Service_Manager::ACE_Service_Manager"); +} + +int +ACE_Service_Manager::suspend (void) +{ + ACE_TRACE ("ACE_Service_Manager::suspend"); + return ACE_Reactor::instance ()->suspend_handler (this); +} + +int +ACE_Service_Manager::resume (void) +{ + ACE_TRACE ("ACE_Service_Manager::resume"); + return ACE_Reactor::instance ()->resume_handler (this); +} + +int +ACE_Service_Manager::open (const ACE_INET_Addr &sia) +{ + ACE_TRACE ("ACE_Service_Manager::open"); + + // Reuse the listening address, even if it's already in use! + if (this->acceptor_.open (sia, 1) == -1) + return -1; + return 0; +} + +int +ACE_Service_Manager::info (ACE_TCHAR **strp, size_t length) const +{ + ACE_TRACE ("ACE_Service_Manager::info"); + ACE_INET_Addr sa; + ACE_TCHAR buf[BUFSIZ]; + + if (this->acceptor_.get_local_addr (sa) == -1) + return -1; + + ACE_OS::sprintf (buf, + ACE_LIB_TEXT ("%d/%s %s"), + sa.get_port_number (), + ACE_LIB_TEXT ("tcp"), + ACE_LIB_TEXT ("# lists all services in the daemon\n")); + if (*strp == 0 && (*strp = ACE_OS::strdup (buf)) == 0) + return -1; + else + ACE_OS::strsncpy (*strp, buf, length); + return ACE_OS::strlen (buf); +} + +int +ACE_Service_Manager::init (int argc, ACE_TCHAR *argv[]) +{ + ACE_TRACE ("ACE_Service_Manager::init"); + ACE_INET_Addr local_addr (ACE_Service_Manager::DEFAULT_PORT_); + ACE_Get_Opt getopt (argc, argv, ACE_LIB_TEXT ("dp:s:"), 0); // Start at argv[0] + + for (int c; (c = getopt ()) != -1; ) + switch (c) + { + case 'd': + this->debug_ = 1; + break; + case 'p': + local_addr.set ((u_short) ACE_OS::atoi (getopt.opt_arg ())); + break; + case 's': + this->signum_ = ACE_OS::atoi (getopt.opt_arg ()); + break; + default: + break; + } + + if (this->get_handle () == ACE_INVALID_HANDLE && + this->open (local_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("open")), -1); + else if (ACE_Reactor::instance ()->register_handler + (this, + ACE_Event_Handler::ACCEPT_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("registering service with ACE_Reactor\n")), + -1); + return 0; +} + +int +ACE_Service_Manager::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_TRACE ("ACE_Service_Manager::handle_close"); + return this->acceptor_.close (); +} + +int +ACE_Service_Manager::fini (void) +{ + ACE_TRACE ("ACE_Service_Manager::fini"); + + int retv = 0; + if (this->get_handle () != ACE_INVALID_HANDLE) + { + retv = ACE_Reactor::instance ()->remove_handler + (this, + ACE_Event_Handler::ACCEPT_MASK | + ACE_Event_Handler::DONT_CALL); + this->handle_close (ACE_INVALID_HANDLE, + ACE_Event_Handler::NULL_MASK); + } + return retv; +} + +ACE_HANDLE +ACE_Service_Manager::get_handle (void) const +{ + ACE_TRACE ("ACE_Service_Manager::get_handle"); + return this->acceptor_.get_handle (); +} + +int +ACE_Service_Manager::handle_signal (int, siginfo_t *, ucontext_t *) +{ + return 0; +} + +// Determine all the services offered by this daemon and return the +// information back to the client. + +int +ACE_Service_Manager::list_services (void) +{ + ACE_TRACE ("ACE_Service_Manager::list_services"); + ACE_Service_Repository_Iterator sri (*ACE_Service_Repository::instance (), 0); + + for (const ACE_Service_Type *sr; + sri.next (sr) != 0; + sri.advance ()) + { + int len = ACE_OS::strlen (sr->name ()) + 11; + ACE_TCHAR buf[BUFSIZ]; + ACE_TCHAR *p = buf + len; + + ACE_OS::strcpy (buf, sr->name ()); + ACE_OS::strcat (buf, (sr->active ()) ? + ACE_LIB_TEXT (" (active) ") : + ACE_LIB_TEXT (" (paused) ")); + + p[-1] = ' '; + p[0] = '\0'; + + len += sr->type ()->info (&p, sizeof buf - len); + + if (this->debug_) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("len = %d, info = %s%s"), + len, + buf, + buf[len - 1] == '\n' ? ACE_LIB_TEXT ("") : ACE_LIB_TEXT ("\n"))); + + if (len > 0) + { + ssize_t n = this->client_stream_.send_n (buf, + len); + + if (n != len || (n == -1 && errno != EPIPE)) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("send_n"))); + } + } + + return 0; +} + +// Trigger a reconfiguration of the Service Configurator via its +// svc.conf file. + +int +ACE_Service_Manager::reconfigure_services (void) +{ + ACE_TRACE ("ACE_Service_Manager::reconfigure_services"); + +#if 0 +// Send ourselves a signal! ACE_OS::kill (ACE_OS::getpid (), +// this->signum_); +#endif /* 0 */ + + // Flag the main event loop that a reconfiguration should occur. + // The next trip through the should + // pick this up and cause a reconfiguration. Note that we can't + // trigger the reconfiguration automatically since that might "pull + // the rug" out from underneath the existing services in a + // problematic way. + ACE_Service_Config::reconfig_occurred ((sig_atomic_t) 1); + return this->client_stream_.send_n ("done\n", + sizeof ("done\n")); +} + +// isolate the request-processing code +void +ACE_Service_Manager::process_request (ACE_TCHAR *request) +{ + ACE_TRACE("ACE_Service_Manager::process_request"); + ACE_TCHAR *p; + + // Kill trailing newlines. + for (p = request; + (*p != '\0') && (*p != '\r') && (*p != '\n'); + p++) + continue; + + *p = '\0'; + + if (ACE_OS::strcmp (request, ACE_LIB_TEXT ("help")) == 0) + // Return a list of the configured services. + this->list_services (); + else if (ACE_OS::strcmp (request, ACE_LIB_TEXT ("reconfigure") )== 0) + // Trigger a reconfiguration by re-reading the local file. + this->reconfigure_services (); + else + // Just process a single request passed in via the socket + // remotely. + ACE_Service_Config::process_directive (request); + + // Additional management services may be handled here... +} + +// Accept new connection from client and carry out the service they +// request. + +int +ACE_Service_Manager::handle_input (ACE_HANDLE) +{ + ACE_TRACE ("ACE_Service_Manager::handle_input"); + + // 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 = + ACE_Reactor::instance ()->uses_event_associations (); + + if (this->acceptor_.accept (this->client_stream_, // stream + 0, // remote address + 0, // timeout + 1, // restart + reset_new_handle // reset new handler + ) == -1) + return -1; + + if (this->debug_) + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("client_stream fd = %d\n"), + this->client_stream_.get_handle ())); + ACE_INET_Addr sa; + if (this->client_stream_.get_remote_addr (sa) == -1) + return -1; + + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("accepted from host %s at port %d\n"), + sa.get_host_name (), + sa.get_port_number ())); + } + + ACE_TCHAR request[BUFSIZ]; + ACE_TCHAR* offset = request; + ssize_t remaining = sizeof (request); + + // Read service request from client. + + ssize_t result; + + // Keep looping until we actually get the request. Note that Win32 + // sets the socket into non-blocking mode, so we may need to loop if + // the system is heavily loaded. Read bytes into the buffer until a + // '\n' or '\r' is found in the buffer, otherwise the buffer + // contains an incomplete string. + do + { + result = client_stream_.recv (offset, remaining); + + if (result >= 0) + { + if ((remaining -= result) <= 0) + { + ACE_DEBUG ((LM_ERROR, + ACE_LIB_TEXT ("Request buffer overflow.\n"))); + result = 0; + break; + } + + offset += result; + *offset = 0; + + if (ACE_OS::strchr (request, '\r') != 0 + || ACE_OS::strchr (request, '\n') != 0) + remaining = 0; + } + } + while (result == -1 && errno == EWOULDBLOCK || remaining > 0); + + switch (result) + { + case -1: + if (this->debug_) + ACE_DEBUG ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("recv"))); + break; + case 0: + return 0; + /* NOTREACHED */ + default: + { + ACE_Event_Handler *old_signal_handler = 0; + ACE_Reactor::instance ()->register_handler (SIGPIPE, + this, + 0, + &old_signal_handler); + + this->process_request (request); + + // Restore existing SIGPIPE handler + ACE_Reactor::instance ()->register_handler (SIGPIPE, + old_signal_handler); + } + } + + if (this->client_stream_.close () == -1 && this->debug_) + ACE_DEBUG ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("close"))); + return 0; +} diff --git a/ace/Svcconf/Service_Manager.h b/ace/Svcconf/Service_Manager.h new file mode 100644 index 00000000000..2dbb4e5068b --- /dev/null +++ b/ace/Svcconf/Service_Manager.h @@ -0,0 +1,120 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Service_Manager.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SERVICE_MANAGER_H +#define ACE_SERVICE_MANAGER_H +#include "ace/pre.h" + +#include "ace/SOCK_Stream.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/SOCK_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/Service_Object.h" + +/** + * @class ACE_Service_Manager + * + * @brief Provide a standard ACE service for managing all the services + * configured in an . + * + * This implementation is simple and just handles each client + * request one at a time. There are currently 3 types of requests: + * + List services: If the string "help" is sent, return a list of all + * the services supported by the Service Configurator. + * + Reconfigure: If the string "reconfigure" is sent trigger a + * reconfiguration, which will re-read the local file. + * + Process directive: If neither "help" nor "reconfigure" is sent, + * simply treat the incoming string as a process directive and pass + * it along to . This allows + * remote configuration via command-line instructions like + * % echo suspend My_Remote_Service | telnet hostname 3911 + * + * Each request is associated with a new connection, which is closed + * when the request is processed. In addition, you must be using the + * singleton in order to trigger + * reconfigurations. + */ +class ACE_Export ACE_Service_Manager : public ACE_Service_Object +{ +public: + // = Initialization and termination hooks. + /// Constructor. + ACE_Service_Manager (void); + + /// Destructor. + ~ACE_Service_Manager (void); + +protected: + // = Perform the various meta-services. + + /// Trigger a reconfiguration of the Service Configurator by + //re-reading its local file. + virtual int reconfigure_services (void); + + /// Determine all the services offered by this daemon and return the + /// information back to the client. + virtual int list_services (void); + + // = Dynamic linking hooks. + virtual int init (int argc, ACE_TCHAR *argv[]); + virtual int info (ACE_TCHAR **info_string, size_t length) const; + virtual int fini (void); + + // = Scheduling hooks. + virtual int suspend (void); + virtual int resume (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + int open (const ACE_INET_Addr &sia); + + // = Demultiplexing hooks. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE fd); + virtual int handle_close (ACE_HANDLE fd, ACE_Reactor_Mask); + virtual int handle_signal (int signum, siginfo_t *, ucontext_t *); + + /// Handle one request. + virtual void process_request (ACE_TCHAR *request); + + /// Connection to the client (we only support one client connection + /// at a time). + ACE_SOCK_Stream client_stream_; + + /// Acceptor instance. + ACE_SOCK_Acceptor acceptor_; + + /// Keep track of the debugging level. + int debug_; + + /// The signal used to trigger reconfiguration. + int signum_; + + /// Default port for the Acceptor to listen on. + static u_short DEFAULT_PORT_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Service_Manager.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* _SERVICE_MANAGER_H */ diff --git a/ace/Svcconf/Service_Manager.i b/ace/Svcconf/Service_Manager.i new file mode 100644 index 00000000000..a040265d05f --- /dev/null +++ b/ace/Svcconf/Service_Manager.i @@ -0,0 +1,10 @@ +/* -*- C++ -*- */ +// $Id$ + +// Service_Manager.i + +ACE_INLINE +ACE_Service_Manager::~ACE_Service_Manager (void) +{ + ACE_TRACE ("ACE_Service_Manager::~ACE_Service_Manager"); +} diff --git a/ace/Svcconf/Service_Object.cpp b/ace/Svcconf/Service_Object.cpp new file mode 100644 index 00000000000..c5dfa7ce240 --- /dev/null +++ b/ace/Svcconf/Service_Object.cpp @@ -0,0 +1,96 @@ +// $Id$ + +#include "ace/Service_Types.h" +#include "ace/Service_Object.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Service_Object.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Service_Object, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Object) +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Type) + +void +ACE_Service_Type::dump (void) const +{ + ACE_TRACE ("ACE_Service_Type::dump"); +} + +ACE_Service_Type::ACE_Service_Type (const ACE_TCHAR *n, + ACE_Service_Type_Impl *t, + const ACE_SHLIB_HANDLE h, + int active) + : name_ (0), + type_ (t), + handle_ (h), + active_ (active), + fini_already_called_ (0) +{ + ACE_TRACE ("ACE_Service_Type::ACE_Service_Type"); + this->name (n); +} + +ACE_Service_Type::~ACE_Service_Type (void) +{ + ACE_TRACE ("ACE_Service_Type::~ACE_Service_Type"); + + this->fini (); + + if (this->handle_ != 0) + ACE_OS::dlclose ((ACE_SHLIB_HANDLE) this->handle_); + + delete [] (ACE_TCHAR *) this->name_; +} + +void +ACE_Service_Type::fini (void) +{ + if (!this->fini_already_called_) + { + this->type_->fini (); + this->fini_already_called_ = 1; + } +} + +void +ACE_Service_Type::suspend (void) const +{ + ACE_TRACE ("ACE_Service_Type::suspend"); + ((ACE_Service_Type *) this)->active_ = 0; + this->type_->suspend (); +} + +void +ACE_Service_Type::resume (void) const +{ + ACE_TRACE ("ACE_Service_Type::resume"); + ((ACE_Service_Type *) this)->active_ = 1; + this->type_->resume (); +} + +ACE_Service_Object::ACE_Service_Object (ACE_Reactor *r) + : ACE_Event_Handler (r) +{ + ACE_TRACE ("ACE_Service_Object::ACE_Service_Object"); +} + +ACE_Service_Object::~ACE_Service_Object (void) +{ + ACE_TRACE ("ACE_Service_Object::~ACE_Service_Object"); +} + +int +ACE_Service_Object::suspend (void) +{ + ACE_TRACE ("ACE_Service_Object::suspend"); + return 0; +} + +int +ACE_Service_Object::resume (void) +{ + ACE_TRACE ("ACE_Service_Object::resume"); + return 0; +} diff --git a/ace/Svcconf/Service_Object.h b/ace/Svcconf/Service_Object.h new file mode 100644 index 00000000000..2801a8be3c4 --- /dev/null +++ b/ace/Svcconf/Service_Object.h @@ -0,0 +1,164 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Service_Object.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SERVICE_OBJECT_H +#define ACE_SERVICE_OBJECT_H +#include "ace/pre.h" + +#include "ace/Shared_Object.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Event_Handler.h" + +#define ACE_Component ACE_Service_Object +/** + * @class ACE_Service_Object + * + * @brief Provide the abstract base class common to all service + * implementations. + * + * Classes that inherit from are capable + * of being registered with the (due to the + * , as well as being dynamically linked by + * the (due to the ). + */ +class ACE_Export ACE_Service_Object : public ACE_Event_Handler, public ACE_Shared_Object +{ +public: + // = Initialization and termination methods. + /// Constructor. + ACE_Service_Object (ACE_Reactor * = 0); + + /// Destructor. + virtual ~ACE_Service_Object (void); + + /// Temporarily disable a service without removing it completely. + virtual int suspend (void); + + /// Re-enable a previously suspended service. + virtual int resume (void); +}; + +// Forward decl. +class ACE_Service_Type_Impl; + +/** + * @class ACE_Service_Type + * + * @brief Keeps track of information related to the various + * subclasses. + * + * This class acts as the interface of the "Bridge" pattern. + */ +class ACE_Export ACE_Service_Type +{ +public: + enum + { + /// Delete the payload object. + DELETE_OBJ = 1, + + /// Delete the enclosing object. + DELETE_THIS = 2 + }; + + // = Initialization and termination methods. + ACE_Service_Type (const ACE_TCHAR *n, + ACE_Service_Type_Impl *o, + const ACE_SHLIB_HANDLE handle, + int active); + ~ACE_Service_Type (void); + + const ACE_TCHAR *name (void) const; + void name (const ACE_TCHAR *); + + const ACE_Service_Type_Impl *type (void) const; + void type (const ACE_Service_Type_Impl *, + int active = 1); + + ACE_SHLIB_HANDLE handle (void) const; + void handle (const ACE_SHLIB_HANDLE); + + void suspend (void) const; + void resume (void) const; + int active (void) const; + void active (int); + + /// Calls on + void fini (void); + + /// Check if the service has been fini'ed. + int fini_called (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Humanly readible name of svc. + const ACE_TCHAR *name_; + + /// Pointer to C++ object that implements the svc. + const ACE_Service_Type_Impl *type_; + + /// Handle to shared object file (non-zero if dynamically linked). + ACE_SHLIB_HANDLE handle_; + + /// 1 if svc is currently active, otherwise 0. + int active_; + + /// 1 if on has already been called, otherwise 0. + int fini_already_called_; +}; + +/** + * @class ACE_Service_Object_Ptr + * + * @brief This is a smart pointer that holds onto the associated + * * until the current scope is left, at + * which point the object's hook is called and the + * service_object_ gets deleted. + * + * This class is similar to the Standard C++ Library class + * . It is used in conjunction with statically linked + * , as shown in the + * ./netsvcs/server/main.cpp example. + */ +class ACE_Export ACE_Service_Object_Ptr +{ +public: + // = Initialization and termination methods. + /// Acquire ownership of the . + ACE_Service_Object_Ptr (ACE_Service_Object *so); + + /// Release the held by calling its hook. + ~ACE_Service_Object_Ptr (void); + + /// Smart pointer to access the underlying . + ACE_Service_Object *operator-> (); + +private: + /// Holds the service object until we're done. + ACE_Service_Object *service_object_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Service_Object.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_SERVICE_OBJECT_H */ diff --git a/ace/Svcconf/Service_Object.i b/ace/Svcconf/Service_Object.i new file mode 100644 index 00000000000..7b735cf6c1f --- /dev/null +++ b/ace/Svcconf/Service_Object.i @@ -0,0 +1,87 @@ +/* -*- C++ -*- */ +// $Id$ + +// Service_Object.i + +ACE_INLINE ACE_Service_Object_Ptr::ACE_Service_Object_Ptr (ACE_Service_Object *so) + : service_object_ (so) +{ +} + +ACE_INLINE ACE_Service_Object_Ptr::~ACE_Service_Object_Ptr (void) +{ + this->service_object_->fini (); + delete this->service_object_; +} + +ACE_INLINE ACE_Service_Object * +ACE_Service_Object_Ptr::operator-> () +{ + return this->service_object_; +} + +ACE_INLINE const ACE_TCHAR * +ACE_Service_Type::name (void) const +{ + ACE_TRACE ("ACE_Service_Type::name"); + return this->name_; +} + +ACE_INLINE const ACE_Service_Type_Impl * +ACE_Service_Type::type (void) const +{ + ACE_TRACE ("ACE_Service_Type::type"); + return this->type_; +} + +ACE_INLINE ACE_SHLIB_HANDLE +ACE_Service_Type::handle (void) const +{ + ACE_TRACE ("ACE_Service_Type::handle"); + return this->handle_; +} + +ACE_INLINE void +ACE_Service_Type::name (const ACE_TCHAR *n) +{ + ACE_TRACE ("ACE_Service_Type::name"); + + delete [] (ACE_TCHAR *) this->name_; + this->name_ = ACE::strnew (n); +} + +ACE_INLINE void +ACE_Service_Type::type (const ACE_Service_Type_Impl *o, int enabled) +{ + ACE_TRACE ("ACE_Service_Type::type"); + this->type_ = o; + ((ACE_Service_Type *) this)->active_ = enabled; +} + +ACE_INLINE void +ACE_Service_Type::handle (const ACE_SHLIB_HANDLE h) +{ + ACE_TRACE ("ACE_Service_Type::handle"); + this->handle_ = h; +} + +ACE_INLINE int +ACE_Service_Type::active (void) const +{ + ACE_TRACE ("ACE_Service_Type::active"); + return this->active_ != 0; +} + +ACE_INLINE void +ACE_Service_Type::active (int turnon) +{ + ACE_TRACE ("ACE_Service_Type::active"); + this->active_ = turnon; +} + +ACE_INLINE int +ACE_Service_Type::fini_called (void) const +{ + ACE_TRACE ("ACE_Service_Type::fini_called"); + return this->fini_already_called_; +} diff --git a/ace/Svcconf/Service_Repository.cpp b/ace/Svcconf/Service_Repository.cpp new file mode 100644 index 00000000000..21e7c47c6e6 --- /dev/null +++ b/ace/Svcconf/Service_Repository.cpp @@ -0,0 +1,416 @@ +// Service_Repository.cpp +// $Id$ + +#include "ace/Service_Repository.h" +#include "ace/Object_Manager.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Service_Repository.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Service_Repository, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Repository) + +// Process-wide Service Repository. +ACE_Service_Repository *ACE_Service_Repository::svc_rep_ = 0; + +// Controls whether the Service_Repository is deleted when we shut +// down (we can only delete it safely if we created it)! +int ACE_Service_Repository::delete_svc_rep_ = 0; + +void +ACE_Service_Repository::dump (void) const +{ + ACE_TRACE ("ACE_Service_Repository::dump"); +} + +ACE_Service_Repository::ACE_Service_Repository (void) + : service_vector_ (0), + current_size_ (0), + total_size_ (0) +{ + ACE_TRACE ("ACE_Service_Repository::ACE_Service_Repository"); +} + +ACE_Service_Repository * +ACE_Service_Repository::instance (int size /* = ACE_Service_Repository::DEFAULT_SIZE */) +{ + ACE_TRACE ("ACE_Service_Repository::instance"); + + if (ACE_Service_Repository::svc_rep_ == 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_Service_Repository::svc_rep_ == 0) + { + if (ACE_Object_Manager::starting_up () || + !ACE_Object_Manager::shutting_down ()) + { + ACE_NEW_RETURN (ACE_Service_Repository::svc_rep_, + ACE_Service_Repository (size), + 0); + ACE_Service_Repository::delete_svc_rep_ = 1; + } + } + } + + return ACE_Service_Repository::svc_rep_; +} + +ACE_Service_Repository * +ACE_Service_Repository::instance (ACE_Service_Repository *s) +{ + ACE_TRACE ("ACE_Service_Repository::instance"); + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + ACE_Service_Repository *t = ACE_Service_Repository::svc_rep_; + // We can't safely delete it since we don't know who created it! + ACE_Service_Repository::delete_svc_rep_ = 0; + + ACE_Service_Repository::svc_rep_ = s; + return t; +} + +void +ACE_Service_Repository::close_singleton (void) +{ + ACE_TRACE ("ACE_Service_Repository::close_singleton"); + + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance ())); + + if (ACE_Service_Repository::delete_svc_rep_) + { + delete ACE_Service_Repository::svc_rep_; + ACE_Service_Repository::svc_rep_ = 0; + ACE_Service_Repository::delete_svc_rep_ = 0; + } +} + +// Initialize the Repository to a clean slate. + +int +ACE_Service_Repository::open (int size) +{ + ACE_TRACE ("ACE_Service_Repository::open"); + + ACE_Service_Type **temp; + + ACE_NEW_RETURN (temp, + ACE_Service_Type *[size], + -1); + + this->service_vector_ = ACE_const_cast (const ACE_Service_Type **, + temp); + this->total_size_ = size; + return 0; +} + +ACE_Service_Repository::ACE_Service_Repository (int size) + : current_size_ (0) +{ + ACE_TRACE ("ACE_Service_Repository::ACE_Service_Repository"); + + if (this->open (size) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Service_Repository"))); +} + +// Finalize (call and possibly delete) all the services. + +int +ACE_Service_Repository::fini (void) +{ + ACE_TRACE ("ACE_Service_Repository::fini"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (this->service_vector_ != 0) + { + // the services in reverse order. Note that if services + // were removed from the middle of the repository the order + // won't necessarily be maintained since the method + // performs compaction. However, the common case is not to + // remove services, so typically they are deleted in reverse + // order. + + for (int i = this->current_size_ - 1; i >= 0; i--) + { + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("finalizing %s\n"), + this->service_vector_[i]->name ())); + ACE_Service_Type *s = + ACE_const_cast (ACE_Service_Type *, + this->service_vector_[i]); + s->fini (); + } + } + + return 0; +} + +// Close down all the services. + +int +ACE_Service_Repository::close (void) +{ + ACE_TRACE ("ACE_Service_Repository::close"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (this->service_vector_ != 0) + { + // Delete services in reverse order. Note that if services were + // removed from the middle of the repository the order won't + // necessarily be maintained since the method performs + // compaction. However, the common case is not to remove + // services, so typically they are deleted in reverse order. + + for (int i = this->current_size_ - 1; i >= 0; i--) + { + ACE_Service_Type *s = ACE_const_cast (ACE_Service_Type *, + this->service_vector_[i]); + --this->current_size_; + delete s; + } + + delete [] this->service_vector_; + this->service_vector_ = 0; + this->current_size_ = 0; + } + + return 0; +} + +ACE_Service_Repository::~ACE_Service_Repository (void) +{ + ACE_TRACE ("ACE_Service_Repository::~ACE_Service_Repository"); + this->close (); +} + +// Locate an entry with in the table. If is +// set then only consider services marked as resumed. If the caller +// wants the located entry, pass back a pointer to the located entry +// via . If is not found -1 is returned. If is +// found, but it is suspended and the caller wants to ignore suspended +// services a -2 is returned. Must be called with locks held. + +int +ACE_Service_Repository::find_i (const ACE_TCHAR name[], + const ACE_Service_Type **srp, + int ignore_suspended) +{ + ACE_TRACE ("ACE_Service_Repository::find_i"); + int i; + + for (i = 0; i < this->current_size_; i++) + if (ACE_OS::strcmp (name, + this->service_vector_[i]->name ()) == 0) + break; + + if (i < this->current_size_) + { + if (this->service_vector_[i]->fini_called ()) + { + if (srp != 0) + *srp = 0; + return -1; + } + + if (srp != 0) + *srp = this->service_vector_[i]; + if (ignore_suspended + && this->service_vector_[i]->active () == 0) + return -2; + return i; + } + else + return -1; +} + +int +ACE_Service_Repository::find (const ACE_TCHAR name[], + const ACE_Service_Type **srp, + int ignore_suspended) +{ + ACE_TRACE ("ACE_Service_Repository::find"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + return this->find_i (name, srp, ignore_suspended); +} + + +// Insert the ACE_Service_Type SR into the repository. Note that +// services may be inserted either resumed or suspended. + +int +ACE_Service_Repository::insert (const ACE_Service_Type *sr) +{ + ACE_TRACE ("ACE_Service_Repository::insert"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + int i; + + // Check to see if this is a duplicate. + for (i = 0; i < this->current_size_; i++) + if (ACE_OS::strcmp (sr->name (), + this->service_vector_[i]->name ()) == 0) + break; + + // Replacing an existing entry + if (i < this->current_size_) + { + // Check for self-assignment... + if (sr == this->service_vector_[i]) + return 0; + ACE_Service_Type *s = ACE_const_cast (ACE_Service_Type *, + this->service_vector_[i]); + delete s; + this->service_vector_[i] = sr; + return 0; + } + // Adding a new entry. + else if (i < this->total_size_) + { + this->service_vector_[i] = sr; + this->current_size_++; + return 0; + } + + return -1; +} + +// Re-resume a service that was previously suspended. + +int +ACE_Service_Repository::resume (const ACE_TCHAR name[], + const ACE_Service_Type **srp) +{ + ACE_TRACE ("ACE_Service_Repository::resume"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int i = this->find_i (name, srp, 0); + + if (i == -1) + return -1; + + this->service_vector_[i]->resume (); + return 0; +} + +// Suspend a service so that it will not be considered active under +// most circumstances by other portions of the ACE_Service_Repository. + +int +ACE_Service_Repository::suspend (const ACE_TCHAR name[], + const ACE_Service_Type **srp) +{ + ACE_TRACE ("ACE_Service_Repository::suspend"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + int i = this->find_i (name, srp, 0); + + if (i == -1) + return -1; + + this->service_vector_[i]->suspend (); + return 0; +} + +// Completely remove a entry from the Repository and +// dynamically unlink it if it was originally dynamically linked. +// Since the order of services in the Respository does not matter, we +// simply overwrite the entry being deleted with the final entry in +// the array and decrement the by 1. + +int +ACE_Service_Repository::remove (const ACE_TCHAR name[]) +{ + ACE_TRACE ("ACE_Service_Repository::remove"); + ACE_Service_Type *s = 0; + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + int i = this->find_i (name, 0, 0); + + if (i == -1) + return -1; + + s = ACE_const_cast (ACE_Service_Type *, + this->service_vector_[i]); + --this->current_size_; + + if (this->current_size_ >= 1) + this->service_vector_[i] + = this->service_vector_[this->current_size_]; + } + delete s; + return 0; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Repository_Iterator) + +void +ACE_Service_Repository_Iterator::dump (void) const +{ + ACE_TRACE ("ACE_Service_Repository_Iterator::dump"); +} + +// Initializes the iterator and skips over any suspended entries at +// the beginning of the table, if necessary. Note, you must not +// perform destructive operations on elements during this iteration... + +ACE_Service_Repository_Iterator::ACE_Service_Repository_Iterator + (ACE_Service_Repository &sr, + int ignr_suspended) + : svc_rep_ (sr), + next_ (-1), + ignore_suspended_ (ignr_suspended) +{ + this->advance (); +} + +// Obtains a pointer to the next valid service in the table. If there +// are no more entries, returns 0, else 1. + +int +ACE_Service_Repository_Iterator::next (const ACE_Service_Type *&sr) +{ + ACE_TRACE ("ACE_Service_Repository_Iterator::next"); + if (this->next_ < this->svc_rep_.current_size_) + { + sr = this->svc_rep_.service_vector_[this->next_]; + return 1; + } + else + return 0; +} + +int +ACE_Service_Repository_Iterator::done (void) const +{ + ACE_TRACE ("ACE_Service_Repository_Iterator::done"); + + return this->next_ >= this->svc_rep_.current_size_; +} + +// Advance the iterator by the proper amount. If we are ignoring +// suspended entries and the current entry is suspended, then we must +// skip over this entry. Otherwise, we must advance the NEXT index to +// reference the next valid service entry. + +int +ACE_Service_Repository_Iterator::advance (void) +{ + ACE_TRACE ("ACE_Service_Repository_Iterator::advance"); + + for (++this->next_; + this->next_ < this->svc_rep_.current_size_ + && this->ignore_suspended_ + && this->svc_rep_.service_vector_[this->next_]->active () == 0; + this->next_++) + continue; + + return this->next_ < this->svc_rep_.current_size_; +} diff --git a/ace/Svcconf/Service_Repository.h b/ace/Svcconf/Service_Repository.h new file mode 100644 index 00000000000..46685807437 --- /dev/null +++ b/ace/Svcconf/Service_Repository.h @@ -0,0 +1,203 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Service_Repository.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SERVICE_REPOSITORY_H +#define ACE_SERVICE_REPOSITORY_H +#include "ace/pre.h" + +#include "ace/Service_Types.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#define ACE_Component_Repository ACE_Service_Repository +/** + * @class ACE_Service_Repository + * + * @brief Contains all the services offered by a Service + * Configurator-based application. + * + * This class contains a vector of *'s and + * allows an administrative entity to centrally manage and + * control the behavior of application services. Note that if + * services are removed from the middle of the repository the + * order won't necessarily be maintained since the + * method performs compaction. However, the common case is not + * to remove services, so typically they are deleted in the + * reverse order that they were added originally. + */ +class ACE_Export ACE_Service_Repository +{ +public: + friend class ACE_Service_Repository_Iterator; + + enum + { + DEFAULT_SIZE = ACE_DEFAULT_SERVICE_REPOSITORY_SIZE + }; + + // = Initialization and termination methods. + /// Initialize the repository. + ACE_Service_Repository (void); + + /// Initialize the repository. + ACE_Service_Repository (int size); + + /// Initialize the repository. + int open (int size = DEFAULT_SIZE); + + /// Close down the repository and free up dynamically allocated + /// resources. + ~ACE_Service_Repository (void); + + /// Close down the repository and free up dynamically allocated + /// resources. + int close (void); + + /// Finalize all the services by calling and deleting + /// dynamically allocated services. + int fini (void); + + /// Get pointer to a process-wide . + static ACE_Service_Repository *instance (int size = ACE_Service_Repository::DEFAULT_SIZE); + + /// Set pointer to a process-wide and return + /// existing pointer. + static ACE_Service_Repository *instance (ACE_Service_Repository *); + + /// Delete the dynamically allocated Singleton. + static void close_singleton (void); + + // = Search structure operations (all acquire locks as necessary). + + /// Insert a new service record. Returns -1 when the service repository is full + /// and 0 on success. + int insert (const ACE_Service_Type *); + + /** + * Locate an entry with in the table. If + * is set then only consider services marked as resumed. If the + * caller wants the located entry, pass back a pointer to the + * located entry via . If is not found, -1 is returned. + * If is found, but it is suspended and the caller wants to + * ignore suspended services a -2 is returned. + */ + int find (const ACE_TCHAR name[], + const ACE_Service_Type **srp = 0, + int ignore_suspended = 1); + + /// Remove an existing service record. + int remove (const ACE_TCHAR[]); + + // = Liveness control + /// Resume a service record. + int resume (const ACE_TCHAR[], const ACE_Service_Type ** = 0); + + /// Suspend a service record. + int suspend (const ACE_TCHAR[], const ACE_Service_Type ** = 0); + + /// Return the current size of the repository. + int current_size (void) const; + + /// Return the total size of the repository. + int total_size (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Locates . Must be called without locks being + /// held... + int find_i (const ACE_TCHAR service_name[], + const ACE_Service_Type ** = 0, + int ignore_suspended = 1); + + /// Contains all the configured services. + const ACE_Service_Type **service_vector_; + + /// Current number of services. + int current_size_; + + /// Maximum number of services. + int total_size_; + + /// Pointer to a process-wide . + static ACE_Service_Repository *svc_rep_; + + /// Must delete the if non-0. + static int delete_svc_rep_; + +#if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) + /// Synchronization variable for the MT_SAFE Repository + ACE_Thread_Mutex lock_; +#endif /* ACE_MT_SAFE */ +}; + +/** + * @class ACE_Service_Repository_Iterator + * + * @brief Iterate through the . + * + * Make sure not to delete entries as the iteration is going on + * since this class is not designed as a robust iterator. + */ +class ACE_Export ACE_Service_Repository_Iterator +{ +public: + // = Initialization and termination methods. + /// Constructor initializes the iterator. + ACE_Service_Repository_Iterator (ACE_Service_Repository &sr, + int ignored_suspended = 1); + + /// Destructor. + ~ACE_Service_Repository_Iterator (void); + + // = Iteration methods. + + /// Pass back the that hasn't been seen in the repository. + /// Returns 0 when all items have been seen, else 1. + int next (const ACE_Service_Type *&next_item); + + /// Returns 1 when all items have been seen, else 0. + int done (void) const; + + /// Move forward by one element in the repository. Returns 0 when all the + /// items in the set have been seen, else 1. + int advance (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Reference to the Service Repository we are iterating over. + ACE_Service_Repository &svc_rep_; + + /// Next index location that we haven't yet seen. + int next_; + + /// Are we ignoring suspended services? + int ignore_suspended_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Service_Repository.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* _SERVICE_REPOSITORY_H */ diff --git a/ace/Svcconf/Service_Repository.i b/ace/Svcconf/Service_Repository.i new file mode 100644 index 00000000000..053ff2673a8 --- /dev/null +++ b/ace/Svcconf/Service_Repository.i @@ -0,0 +1,31 @@ +/* -*- C++ -*- */ +// $Id$ + +// Service_Repository.i + +// Returns a count of the number of currently valid entries (counting +// both resumed and suspended entries). + +ACE_INLINE int +ACE_Service_Repository::current_size (void) const +{ + ACE_TRACE ("ACE_Service_Repository::current_size"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->lock_, -1)); + return this->current_size_; +} + +// Returns a count of the total number of possible entries in the +// table. + +ACE_INLINE int +ACE_Service_Repository::total_size (void) const +{ + ACE_TRACE ("ACE_Service_Repository::total_size"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->lock_, -1)); + return this->total_size_; +} + +ACE_INLINE +ACE_Service_Repository_Iterator::~ACE_Service_Repository_Iterator (void) +{ +} diff --git a/ace/Svcconf/Service_Templates.cpp b/ace/Svcconf/Service_Templates.cpp new file mode 100644 index 00000000000..a4f073d3272 --- /dev/null +++ b/ace/Svcconf/Service_Templates.cpp @@ -0,0 +1,74 @@ +// $Id$ + +#include "ace/Service_Templates.h" + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Node; +template class ACE_Unbounded_Set; +template class ACE_Unbounded_Set_Iterator; +template class ACE_Node; +template class ACE_Unbounded_Queue; +template class ACE_Unbounded_Queue_Iterator; +template class ACE_Unbounded_Set; +template class ACE_Unbounded_Set_Iterator; +template class auto_ptr; +template class ACE_Auto_Basic_Ptr; + +template class ACE_Message_Queue; +template class ACE_Message_Queue_Iterator; +template class ACE_Message_Queue_Reverse_Iterator; +template class ACE_Message_Queue_Factory; +template class ACE_Dynamic_Message_Queue; +template class ACE_Module; +template class ACE_Stream; +template class ACE_Stream_Head; +template class ACE_Stream_Tail; +template class ACE_Task; +template class ACE_Thru_Task; + +// Even with threads, these ACE_NULL_SYNCH specializations are necessary. +#if defined (ACE_HAS_THREADS) + template class ACE_Message_Queue; + template class ACE_Message_Queue_Iterator; + template class ACE_Message_Queue_Reverse_Iterator; + template class ACE_Message_Queue_Factory; + template class ACE_Dynamic_Message_Queue; + template class ACE_Module; + template class ACE_Task; + template class ACE_Thru_Task; +#endif /* ACE_HAS_THREADS */ +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Node +#pragma instantiate ACE_Unbounded_Set +#pragma instantiate ACE_Unbounded_Set_Iterator +#pragma instantiate ACE_Node +#pragma instantiate ACE_Unbounded_Queue +#pragma instantiate ACE_Unbounded_Queue_Iterator +#pragma instantiate ACE_Unbounded_Set +#pragma instantiate ACE_Unbounded_Set_Iterator +#pragma instantiate auto_ptr +#pragma instantiate ACE_Auto_Basic_Ptr + +#pragma instantiate ACE_Message_Queue +#pragma instantiate ACE_Message_Queue_Iterator +#pragma instantiate ACE_Message_Queue_Reverse_Iterator +#pragma instantiate ACE_Message_Queue_Factory +#pragma instantiate ACE_Dynamic_Message_Queue +#pragma instantiate ACE_Module +#pragma instantiate ACE_Stream +#pragma instantiate ACE_Stream_Head +#pragma instantiate ACE_Stream_Tail +#pragma instantiate ACE_Task +#pragma instantiate ACE_Thru_Task +// Even with threads, these ACE_NULL_SYNCH specializations are necessary. +#if defined (ACE_HAS_THREADS) + #pragma instantiate ACE_Message_Queue + #pragma instantiate ACE_Message_Queue_Iterator + #pragma instantiate ACE_Message_Queue_Reverse_Iterator + #pragma instantiate ACE_Message_Queue_Factory + #pragma instantiate ACE_Dynamic_Message_Queue + #pragma instantiate ACE_Module + #pragma instantiate ACE_Task + #pragma instantiate ACE_Thru_Task +#endif /* ACE_HAS_THREADS */ +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Svcconf/Service_Templates.h b/ace/Svcconf/Service_Templates.h new file mode 100644 index 00000000000..939081f7813 --- /dev/null +++ b/ace/Svcconf/Service_Templates.h @@ -0,0 +1,29 @@ + +//============================================================================= +/** + * @file Service_Templates.h + * + * $Id$ + * + * @author Priyanka Gontla + */ +//============================================================================= + + +#ifndef ACE_SERVICE_TEMPLATES_H +#define ACE_SERVICE_TEMPLATES_H +#include "ace/pre.h" + +#include "ace/Svc_Conf.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Auto_Ptr.h" +#include "ace/Thread_Manager.h" +#include "ace/Stream_Modules.h" +#include "ace/Stream.h" + +#include "ace/post.h" +#endif /* ACE_SERVICE_TEMPLATES_H */ diff --git a/ace/Svcconf/Service_Types.cpp b/ace/Svcconf/Service_Types.cpp new file mode 100644 index 00000000000..4b66a1cb0d8 --- /dev/null +++ b/ace/Svcconf/Service_Types.cpp @@ -0,0 +1,454 @@ +// $Id$ + +#include "ace/Service_Types.h" +#include "ace/Stream_Modules.h" +#include "ace/Stream.h" + +ACE_RCSID(ace, Service_Types, "$Id$") + +typedef ACE_Stream MT_Stream; +typedef ACE_Module MT_Module; +typedef ACE_Task MT_Task; + +#if !defined (__ACE_INLINE__) +#include "ace/Service_Types.i" +#endif /* __ACE_INLINE__ */ + +ACE_ALLOC_HOOK_DEFINE(ACE_Service_Type_Impl) + +void +ACE_Service_Type_Impl::dump (void) const +{ + ACE_TRACE ("ACE_Service_Type_Impl::dump"); +} + +ACE_Service_Type_Impl::ACE_Service_Type_Impl (void *so, + const ACE_TCHAR *s_name, + u_int f, + ACE_Service_Object_Exterminator gobbler) + : name_ (0), + obj_ (so), + gobbler_ (gobbler), + flags_ (f) +{ + ACE_TRACE ("ACE_Service_Type_Impl::ACE_Service_Type_Impl"); + this->name (s_name); +} + +ACE_Service_Type_Impl::~ACE_Service_Type_Impl (void) +{ + ACE_TRACE ("ACE_Service_Type_Impl::~ACE_Service_Type_Impl"); + + // It's ok to call this, even though we may have already deleted it + // in the fini() method since it would then be NULL. + delete [] (ACE_TCHAR *) this->name_; +} + +int +ACE_Service_Type_Impl::fini (void) const +{ + ACE_TRACE ("ACE_Service_Type_Impl::fini"); + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("destroying %s, flags = %d\n"), + this->name_, + this->flags_)); + + delete [] (ACE_TCHAR *) this->name_; + ((ACE_Service_Type_Impl *) this)->name_ = 0; + + if (ACE_BIT_ENABLED (this->flags_, + ACE_Service_Type::DELETE_OBJ)) + { + if (gobbler_ != 0) + gobbler_ (this->object ()); + else + // Cast to remove const-ness. + operator delete ((void *) this->object ()); + } + + if (ACE_BIT_ENABLED (this->flags_, + ACE_Service_Type::DELETE_THIS)) + delete (ACE_Service_Type_Impl *) this; + + return 0; +} + +ACE_Service_Object_Type::ACE_Service_Object_Type (void *so, + const ACE_TCHAR *s_name, + u_int f, + ACE_Service_Object_Exterminator gobbler) + : ACE_Service_Type_Impl (so, s_name, f, gobbler) +{ + ACE_TRACE ("ACE_Service_Object_Type::ACE_Service_Object_Type"); +} + +int +ACE_Service_Object_Type::init (int argc, ACE_TCHAR *argv[]) const +{ + ACE_TRACE ("ACE_Service_Object_Type::init"); + + void *obj = this->object (); + ACE_Service_Object *so = (ACE_Service_Object *) obj; + + if (so == 0) + return -1; + else + return so->init (argc, argv); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Module_Type) + +void +ACE_Module_Type::dump (void) const +{ + ACE_TRACE ("ACE_Module_Type::dump"); +} + +ACE_Module_Type::ACE_Module_Type (void *m, + const ACE_TCHAR *m_name, + u_int f) + : ACE_Service_Type_Impl (m, m_name, f) +{ + ACE_TRACE ("ACE_Module_Type::ACE_Module_Type"); +} + +int +ACE_Module_Type::init (int argc, ACE_TCHAR *argv[]) const +{ + ACE_TRACE ("ACE_Module_Type::init"); + void *obj = this->object (); + MT_Module *mod = (MT_Module *) obj; + MT_Task *reader = mod->reader (); + MT_Task *writer = mod->writer (); + + if (reader->init (argc, argv) == -1 + || writer->init (argc, argv) == -1) + return -1; + else + return 0; +} + +int +ACE_Module_Type::suspend (void) const +{ + ACE_TRACE ("ACE_Module_Type::suspend"); + void *obj = this->object (); + MT_Module *mod = (MT_Module *) obj; + MT_Task *reader = mod->reader (); + MT_Task *writer = mod->writer (); + + if (reader->suspend () == -1 + || writer->suspend () == -1) + return -1; + else + return 0; +} + +int +ACE_Module_Type::resume (void) const +{ + ACE_TRACE ("ACE_Module_Type::resume"); + void *obj = this->object (); + MT_Module *mod = (MT_Module *) obj; + MT_Task *reader = mod->reader (); + MT_Task *writer = mod->writer (); + + if (reader->resume () == -1 + || writer->resume () == -1) + return -1; + else + return 0; +} + +// Note, these operations are somewhat too familiar with the +// implementation of ACE_Module and ACE_Module::close... + +int +ACE_Module_Type::fini (void) const +{ + ACE_TRACE ("ACE_Module_Type::fini"); + + void *obj = this->object (); + MT_Module *mod = (MT_Module *) obj; + MT_Task *reader = mod->reader (); + MT_Task *writer = mod->writer (); + + if (reader != 0) + reader->fini (); + + if (writer != 0) + writer->fini (); + + // Close the module and delete the memory. + mod->close (MT_Module::M_DELETE); + return ACE_Service_Type_Impl::fini (); +} + +int +ACE_Module_Type::info (ACE_TCHAR **str, size_t len) const +{ + ACE_TRACE ("ACE_Module_Type::info"); + ACE_TCHAR buf[BUFSIZ]; + + ACE_OS::sprintf (buf, + ACE_LIB_TEXT ("%s\t %s"), + this->name (), + ACE_LIB_TEXT ("# ACE_Module\n")); + + if (*str == 0 && (*str = ACE_OS::strdup (buf)) == 0) + return -1; + else + ACE_OS::strsncpy (*str, buf, len); + return ACE_OS::strlen (buf); +} + +void +ACE_Module_Type::link (ACE_Module_Type *n) +{ + ACE_TRACE ("ACE_Module_Type::link"); + this->link_ = n; +} + +ACE_Module_Type * +ACE_Module_Type::link (void) const +{ + ACE_TRACE ("ACE_Module_Type::link"); + return this->link_; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Stream_Type) + +void +ACE_Stream_Type::dump (void) const +{ + ACE_TRACE ("ACE_Stream_Type::dump"); +} + +int +ACE_Stream_Type::init (int, ACE_TCHAR *[]) const +{ + ACE_TRACE ("ACE_Stream_Type::init"); + return 0; +} + +int +ACE_Stream_Type::suspend (void) const +{ + ACE_TRACE ("ACE_Stream_Type::suspend"); + + for (ACE_Module_Type *m = this->head_; + m != 0; + m = m->link ()) + m->suspend (); + + return 0; +} + +int +ACE_Stream_Type::resume (void) const +{ + ACE_TRACE ("ACE_Stream_Type::resume"); + + for (ACE_Module_Type *m = this->head_; + m != 0; + m = m->link ()) + m->resume (); + + return 0; +} + +ACE_Stream_Type::ACE_Stream_Type (void *s, + const ACE_TCHAR *s_name, + u_int f) + : ACE_Service_Type_Impl (s, s_name, f), + head_ (0) +{ + ACE_TRACE ("ACE_Stream_Type::ACE_Stream_Type"); +} + +int +ACE_Stream_Type::info (ACE_TCHAR **str, size_t len) const +{ + ACE_TRACE ("ACE_Stream_Type::info"); + ACE_TCHAR buf[BUFSIZ]; + + ACE_OS::sprintf (buf, + ACE_LIB_TEXT ("%s\t %s"), + this->name (), + ACE_LIB_TEXT ("# STREAM\n")); + + if (*str == 0 && (*str = ACE_OS::strdup (buf)) == 0) + return -1; + else + ACE_OS::strsncpy (*str, buf, len); + return ACE_OS::strlen (buf); +} + +int +ACE_Stream_Type::fini (void) const +{ + ACE_TRACE ("ACE_Stream_Type::fini"); + void *obj = this->object (); + MT_Stream *str = (MT_Stream *) obj; + + for (ACE_Module_Type *m = this->head_; m != 0; ) + { + ACE_Module_Type *t = m->link (); + + // Final arg is an indication to *not* delete the Module. + str->remove (m->name (), + MT_Module::M_DELETE_NONE); + + // Finalize the Module (this may delete it, but we don't really + // care since we don't access it again). + m->fini (); + m = t; + } + + str->close (); + return ACE_Service_Type_Impl::fini (); +} + +// Locate and remove from the ACE_Stream. + +int +ACE_Stream_Type::remove (ACE_Module_Type *mod) +{ + ACE_TRACE ("ACE_Stream_Type::remove"); + + ACE_Module_Type *prev = 0; + void *obj = this->object (); + MT_Stream *str = (MT_Stream *) obj; + int result = 0; + + for (ACE_Module_Type *m = this->head_; m != 0; ) + { + // We need to do this first so we don't bomb out if we delete m! + ACE_Module_Type *link = m->link (); + + if (m == mod) + { + if (prev == 0) + this->head_ = link; + else + prev->link (link); + + // Final arg is an indication to *not* delete the Module. + if (str->remove (m->name (), + MT_Module::M_DELETE_NONE) == -1) + result = -1; + + // This call may end up deleting m, which is ok since we + // don't access it again! + m->fini (); + } + else + prev = m; + + m = link; + } + + return result; +} + +int +ACE_Stream_Type::push (ACE_Module_Type *new_module) +{ + ACE_TRACE ("ACE_Stream_Type::push"); + void *obj = this->object (); + MT_Stream *str = (MT_Stream *) obj; + + new_module->link (this->head_); + this->head_ = new_module; + obj = new_module->object (); + return str->push ((MT_Module *) obj); +} + +ACE_Module_Type * +ACE_Stream_Type::find (const ACE_TCHAR *mod_name) const +{ + ACE_TRACE ("ACE_Stream_Type::find"); + + for (ACE_Module_Type *m = this->head_; + m != 0; + m = m->link ()) + if (ACE_OS::strcmp (m->name (), mod_name) == 0) + return m; + + return 0; +} + +int +ACE_Service_Object_Type::fini (void) const +{ + ACE_TRACE ("ACE_Service_Object_Type::fini"); + + void *obj = this->object (); + + ACE_Service_Object *so = (ACE_Service_Object *) obj; + + if (so) + { + so->fini (); + +#if 0 + if (ACE_BIT_ENABLED (this->flags_, + ACE_Service_Type::DELETE_OBJ)) + delete so; +#endif /* 1 */ + } + + return ACE_Service_Type_Impl::fini (); +} +/* +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Message_Queue; +template class ACE_Message_Queue_Iterator; +template class ACE_Message_Queue_Reverse_Iterator; +template class ACE_Message_Queue_Factory; +template class ACE_Dynamic_Message_Queue; +template class ACE_Module; +template class ACE_Stream; +template class ACE_Stream_Head; +template class ACE_Stream_Tail; +template class ACE_Task; +template class ACE_Thru_Task; + +// Even with threads, these ACE_NULL_SYNCH specializations are necessary. +#if defined (ACE_HAS_THREADS) + template class ACE_Message_Queue; + template class ACE_Message_Queue_Iterator; + template class ACE_Message_Queue_Reverse_Iterator; + template class ACE_Message_Queue_Factory; + template class ACE_Dynamic_Message_Queue; + template class ACE_Module; + template class ACE_Task; + template class ACE_Thru_Task; + #endif *//* ACE_HAS_THREADS */ +/* +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Message_Queue +#pragma instantiate ACE_Message_Queue_Iterator +#pragma instantiate ACE_Message_Queue_Reverse_Iterator +#pragma instantiate ACE_Message_Queue_Factory +#pragma instantiate ACE_Dynamic_Message_Queue +#pragma instantiate ACE_Module +#pragma instantiate ACE_Stream +#pragma instantiate ACE_Stream_Head +#pragma instantiate ACE_Stream_Tail +#pragma instantiate ACE_Task +#pragma instantiate ACE_Thru_Task +// Even with threads, these ACE_NULL_SYNCH specializations are necessary. +#if defined (ACE_HAS_THREADS) + #pragma instantiate ACE_Message_Queue + #pragma instantiate ACE_Message_Queue_Iterator + #pragma instantiate ACE_Message_Queue_Reverse_Iterator + #pragma instantiate ACE_Message_Queue_Factory + #pragma instantiate ACE_Dynamic_Message_Queue + #pragma instantiate ACE_Module + #pragma instantiate ACE_Task + #pragma instantiate ACE_Thru_Task + #endif *//* ACE_HAS_THREADS */ +//#else +//#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Svcconf/Service_Types.h b/ace/Svcconf/Service_Types.h new file mode 100644 index 00000000000..54cac6e4604 --- /dev/null +++ b/ace/Svcconf/Service_Types.h @@ -0,0 +1,196 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Service_Types.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SERVICE_TYPE_H +#define ACE_SERVICE_TYPE_H +#include "ace/pre.h" + +#include "ace/Service_Object.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Synch.h" + +/** + * @class ACE_Service_Type_Impl + * + * @brief The abstract base class of the hierarchy that defines the + * contents of the . The subclasses of + * this class allow the configuration of , + * , and . + * + * This class provides the root of the implementation hierarchy + * of the "Bridge" pattern. It maintains a pointer to the + * appropriate type of service implementation, i.e., + * , , or . + */ +class ACE_Export ACE_Service_Type_Impl +{ +public: + // = Initialization and termination methods. + ACE_Service_Type_Impl (void *object, + const ACE_TCHAR *s_name, + u_int flags = 0, + ACE_Service_Object_Exterminator gobbler = 0); + virtual ~ACE_Service_Type_Impl (void); + + // = Pure virtual interface (must be defined by the subclass). + virtual int suspend (void) const = 0; + virtual int resume (void) const = 0; + virtual int init (int argc, ACE_TCHAR *argv[]) const = 0; + virtual int fini (void) const; + virtual int info (ACE_TCHAR **str, size_t len) const = 0; + + /// The pointer to the service. + void *object (void) const; + + /// Get the name of the service. + const ACE_TCHAR *name (void) const; + + /// Set the name of the service. + void name (const ACE_TCHAR *); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Name of the service. + const ACE_TCHAR *name_; + + /// Pointer to object that implements the service. This actually + /// points to an , , or . + void *obj_; + + /// Destroy function to deallocate obj_. + ACE_Service_Object_Exterminator gobbler_; + + /// Flags that control serivce behavior (particularly deletion). + u_int flags_; +}; + +/** + * @class ACE_Service_Object_Type + * + * @brief Define the methods for handling the configuration of + * . + */ +class ACE_Export ACE_Service_Object_Type : public ACE_Service_Type_Impl +{ +public: + // = Initialization method. + ACE_Service_Object_Type (void *so, + const ACE_TCHAR *name, + u_int flags = 0, + ACE_Service_Object_Exterminator gobbler = 0); + + ~ACE_Service_Object_Type (void); + + // = Implement the hooks for . + virtual int suspend (void) const; + virtual int resume (void) const; + virtual int init (int argc, ACE_TCHAR *argv[]) const; + virtual int fini (void) const; + virtual int info (ACE_TCHAR **str, size_t len) const; +}; + +/** + * @class ACE_Module_Type + * + * @brief Define the methods for handling the configuration of + * . + */ +class ACE_Export ACE_Module_Type : public ACE_Service_Type_Impl +{ +public: + // = Initialization method. + ACE_Module_Type (void *m, // Really an *. + const ACE_TCHAR *identifier, + u_int flags = 0); + + ~ACE_Module_Type (void); + + // = Implement the hooks for . + virtual int suspend (void) const; + virtual int resume (void) const; + virtual int init (int argc, ACE_TCHAR *argv[]) const; + virtual int fini (void) const; + virtual int info (ACE_TCHAR **str, size_t len) const; + + // Get/set the link pointer. + ACE_Module_Type *link (void) const; + void link (ACE_Module_Type *); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Pointer to the next in an . + ACE_Module_Type *link_; +}; + +/** + * @class ACE_Stream_Type + * + * @brief Define the methods for handling the configuration of + * . + */ +class ACE_Export ACE_Stream_Type : public ACE_Service_Type_Impl +{ +public: + // = Initialization method. + ACE_Stream_Type (void *s, // Really an *. + const ACE_TCHAR *identifier, + u_int flags = 0); + + ~ACE_Stream_Type (void); + + // = Implement the hooks for . + virtual int suspend (void) const; + virtual int resume (void) const; + virtual int init (int argc, ACE_TCHAR *argv[]) const; + virtual int fini (void) const; + virtual int info (ACE_TCHAR **str, size_t len) const; + + /// Add a new to the top of the . + int push (ACE_Module_Type *new_module); + + /// Search for and remove it from the . + int remove (ACE_Module_Type *module); + + /// Locate the with . + ACE_Module_Type *find (const ACE_TCHAR *mod_name) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Pointer to the head of the list. + ACE_Module_Type *head_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Service_Types.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* _SERVICE_TYPE_H */ diff --git a/ace/Svcconf/Service_Types.i b/ace/Svcconf/Service_Types.i new file mode 100644 index 00000000000..78eb127cbfc --- /dev/null +++ b/ace/Svcconf/Service_Types.i @@ -0,0 +1,64 @@ +/* -*- C++ -*- */ +// $Id$ + +ACE_INLINE void * +ACE_Service_Type_Impl::object (void) const +{ + ACE_TRACE ("ACE_Service_Type_Impl::object"); + return this->obj_; +} + +ACE_INLINE const ACE_TCHAR * +ACE_Service_Type_Impl::name (void) const +{ + ACE_TRACE ("ACE_Service_Type_Impl::name"); + return this->name_; +} + +ACE_INLINE void +ACE_Service_Type_Impl::name (const ACE_TCHAR *n) +{ + ACE_TRACE ("ACE_Service_Type_Impl::name"); + + delete [] (ACE_TCHAR *) this->name_; + this->name_ = ACE::strnew (n); +} + +ACE_INLINE +ACE_Service_Object_Type::~ACE_Service_Object_Type (void) +{ + ACE_TRACE ("ACE_Service_Object_Type::~ACE_Service_Object_Type"); +} + +ACE_INLINE int +ACE_Service_Object_Type::suspend (void) const +{ + ACE_TRACE ("ACE_Service_Object_Type::suspend"); + return ((ACE_Service_Object *) this->object ())->suspend (); +} + +ACE_INLINE int +ACE_Service_Object_Type::resume (void) const +{ + ACE_TRACE ("ACE_Service_Object_Type::resume"); + return ((ACE_Service_Object *) this->object ())->resume (); +} + +ACE_INLINE int +ACE_Service_Object_Type::info (ACE_TCHAR **str, size_t len) const +{ + ACE_TRACE ("ACE_Service_Object_Type::info"); + return ((ACE_Service_Object *) this->object ())->info (str, len); +} + +ACE_INLINE +ACE_Module_Type::~ACE_Module_Type (void) +{ + ACE_TRACE ("ACE_Module_Type::~ACE_Module_Type"); +} + +ACE_INLINE +ACE_Stream_Type::~ACE_Stream_Type (void) +{ + ACE_TRACE ("ACE_Stream_Type::~ACE_Stream_Type"); +} diff --git a/ace/Svcconf/Shared_Object.cpp b/ace/Svcconf/Shared_Object.cpp new file mode 100644 index 00000000000..b7237bfb1a3 --- /dev/null +++ b/ace/Svcconf/Shared_Object.cpp @@ -0,0 +1,46 @@ +// Shared_Object.cpp +// $Id$ + +#include "ace/Shared_Object.h" +/* Provide the abstract base class used to access dynamic linking + facilities */ + +#if !defined (__ACE_INLINE__) +#include "ace/Shared_Object.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Shared_Object, "$Id$") + +// Initializes object when dynamic linking occurs. + +int +ACE_Shared_Object::init (int, ACE_TCHAR *[]) +{ + ACE_TRACE ("ACE_Shared_Object::init"); + return 0; +} + +// Terminates object when dynamic unlinking occurs. + +int +ACE_Shared_Object::fini (void) +{ + ACE_TRACE ("ACE_Shared_Object::fini"); + return 0; +} + +// Returns information on active object. + +int +ACE_Shared_Object::info (ACE_TCHAR **, size_t) const +{ + ACE_TRACE ("ACE_Shared_Object::info"); + return 0; +} + +// Need to give a default implementation. + +ACE_Shared_Object::~ACE_Shared_Object (void) +{ + ACE_TRACE ("ACE_Shared_Object::~ACE_Shared_Object"); +} diff --git a/ace/Svcconf/Shared_Object.h b/ace/Svcconf/Shared_Object.h new file mode 100644 index 00000000000..dc6d1ed7480 --- /dev/null +++ b/ace/Svcconf/Shared_Object.h @@ -0,0 +1,51 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Shared_Object.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SHARED_OBJECT_H +#define ACE_SHARED_OBJECT_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_Shared_Object + * + * @brief Provide the abstract base class used to access dynamic + * linking facilities. + */ +class ACE_Export ACE_Shared_Object +{ +public: + ACE_Shared_Object (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); + + /// Returns information on a service object. + virtual int info (ACE_TCHAR **info_string, size_t length = 0) const; + + virtual ~ACE_Shared_Object (void); +}; + +#if defined (__ACE_INLINE__) +#include "ace/Shared_Object.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_SHARED_OBJECT_H */ diff --git a/ace/Svcconf/Shared_Object.i b/ace/Svcconf/Shared_Object.i new file mode 100644 index 00000000000..97ca0090c6d --- /dev/null +++ b/ace/Svcconf/Shared_Object.i @@ -0,0 +1,9 @@ +/* -*- C++ -*- */ +// $Id$ + +// Shared_Object.i + +ACE_INLINE +ACE_Shared_Object::ACE_Shared_Object (void) +{ +} diff --git a/ace/Svcconf/Svc_Conf.h b/ace/Svcconf/Svc_Conf.h new file mode 100644 index 00000000000..ecf285baebf --- /dev/null +++ b/ace/Svcconf/Svc_Conf.h @@ -0,0 +1,208 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Svc_Conf.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_SVC_CONF_H +#define ACE_SVC_CONF_H + +#include "ace/pre.h" + +// Globally visible macros, type decls, and extern var decls for +// Service Configurator utility. + +#include "ace/Obstack.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Service_Config.h" +#include "ace/Parse_Node.h" + +// Forward declarations. +struct ace_yy_buffer_state; + +// The following yylex() declarations require support for reentrant +// parser generation (e.g. from GNU Bison). +#if defined (DEBUGGING) +#if defined (ACE_YY_DECL) +#undef ACE_YY_DECL +#endif /* ACE_YY_DECL */ +#define ACE_YY_DECL extern "C" char *ace_yylex (ACE_YYSTYPE *ace_yylval, void *ACE_YYLEX_PARAM) +#else +#define ACE_YY_DECL extern "C" int ace_yylex (ACE_YYSTYPE *ace_yylval, void *ACE_YYLEX_PARAM) +#endif /* DEBUGGING */ + +extern void ace_yy_delete_buffer (ace_yy_buffer_state *buffer); + +/** + * @class ACE_Svc_Conf_Param + * + * @brief An instance of this object will be passed down to the + * yyparse() and yylex() functions. + * + * This class retains the state for a given parse/scan. It primarily + * makes it possible to hold the static object lock in the scanner + * for as short a period of time as possible. The resulting finer + * grained locking prevents deadlocks from occuring when scanning a + * `svc.conf' file and activating an ACE_Task, for example, as a + * result of processing the directives in that file. + */ +class ACE_Svc_Conf_Param +{ +public: + + enum SVC_CONF_PARAM_TYPE + { + /// The lexer will scan a file containing one or more directives. + SVC_CONF_FILE, + + /// The lexer will scan a string containing a directive. + SVC_CONF_DIRECTIVE + }; + + /// Constructor + ACE_Svc_Conf_Param (FILE *file) + : type (SVC_CONF_FILE), + yyerrno (0), + yylineno (1), + buffer (0), + obstack () + { + source.file = file; + } + + /// Constructor + ACE_Svc_Conf_Param (const ACE_TCHAR *directive) + : type (SVC_CONF_DIRECTIVE), + yyerrno (0), + yylineno (1), + buffer (0), + obstack () + { + source.directive = directive; + } + + ~ACE_Svc_Conf_Param (void) + { + ace_yy_delete_buffer (this->buffer); + } + +public: + + union + { + + /// FILE stream from which directives will be scanned and parsed. + FILE *file; + + /// String containing directive that will be scanned and parsed. + const ACE_TCHAR *directive; + + } source; + + // Discriminant use to determine which union member to use. + SVC_CONF_PARAM_TYPE type; + + /// Keeps track of the number of errors encountered so far. + int yyerrno; + + /// Keeps track of the current line number for error-handling routine. + int yylineno; + + /// Lexer buffer that corresponds to the current Service + /// Configurator file/direct scan. + ace_yy_buffer_state *buffer; + + /// Obstack used for efficient memory allocation when + /// parsing/scanning a service configurator directive. + ACE_Obstack_T obstack; + +}; + +// Parameter that is passed down to the yyparse() function, and +// eventually to yylex(). +#define ACE_YYPARSE_PARAM ace_svc_conf_parameter +#define ACE_YYLEX_PARAM ACE_YYPARSE_PARAM + +#define ACE_SVC_CONF_PARAM (ACE_static_cast (ACE_Svc_Conf_Param *, ACE_YYLEX_PARAM)) + +// The following definition for the ACE_YYSTYPE must occur before +// ACE_YY_DECL is declared since ACE_YY_DECL expands to function +// prototypes that use ACE_YYSTYPE. +typedef union +{ + int type_; + ACE_Location_Node *location_node_; + ACE_Parse_Node *parse_node_; + ACE_Static_Node *static_node_; + ACE_Service_Type *svc_record_; + ACE_TCHAR *ident_; +} ACE_YYSTYPE; + +// Forward declaration +struct ace_yy_buffer_state; + +/// Create and push a new lexer buffer on to the buffer stack for use +/// when scanning the given file. +void ace_yy_push_buffer (FILE *file, + ace_yy_buffer_state *&buffer); + +/// Create and push a new lexer buffer on to the buffer stack for use +/// when scanning the given directive. +void ace_yy_push_buffer (const ACE_TCHAR *directive, + ace_yy_buffer_state *&buffer); + +/// Pop the current lexer buffer off of the buffer stack and +/// deallocate it. +void ace_yy_pop_buffer (ace_yy_buffer_state *buf); + +/// Performs the parsing +#ifdef ACE_YYPARSE_PARAM +int ace_yyparse (void *); +#else +int ace_yyparse (void); +#endif + +/// Performs the lexical analysis +ACE_YY_DECL; + +/// Name of input stream +extern FILE *ace_yyin; + +/// Error handling routine required by YACC or BISON +void ace_yyerror (const ACE_TCHAR *); + +/// Keeps track of the current line number for error-handling routine +extern int ace_yylineno; + +/// Keeps track of the number of errors encountered so far +extern int ace_yyerrno; + +/// Holds the lexeme for the current token +extern ACE_TCHAR *ace_yytext; + +/// Holds the length of the lexeme for the current token +extern int ace_yyleng; + +/// Factory that creates a new ACE_Service_Type_Impl. +extern ACE_Service_Type_Impl * +ace_create_service_type (const ACE_TCHAR *, + int, + void *, + unsigned int, + ACE_Service_Object_Exterminator = 0); + + +#include "ace/post.h" + +#endif /* ACE_SVC_CONF_H */ diff --git a/ace/Svcconf/Svc_Conf.l b/ace/Svcconf/Svc_Conf.l new file mode 100644 index 00000000000..bb1f3e2dc0c --- /dev/null +++ b/ace/Svcconf/Svc_Conf.l @@ -0,0 +1,140 @@ +%{ +// $Id$ +// Sample lexical analysis for regular expression subset. Must be +// compiled with FLEX and an ANSI C++ compiler. + +// Lexical tokens values defined by YACC. +#include "ace/Svc_Conf.h" +#include "ace/Svc_Conf_Tokens.h" +#include "ace/Svc_Conf_Lexer_Guard.h" + +ACE_RCSID (ace, + Svc_Conf_l, + "$Id$") + +// Keeps track of the current line for debugging output. +int yylineno = 1; + +#define token(x) x +%} + +%s PARAMETERS +%s NORMAL + +letter [a-zA-Z_] +letter_or_digit [a-zA-Z_0-9] +digit [0-9] +ident {letter}{letter_or_digit}* +pathname ([A-Za-z\%]:)?[a-zA-Z_0-9/\%\.\\~-]+ +symbol [ -~] +string (\"{symbol}*\"|\'{symbol}*\') +white_space [ \t] +newline \n +other . + +%% + +^#{other}*$ ; /* EMPTY */ +dynamic { return token (ACE_DYNAMIC); } +static { return token (ACE_STATIC); } +suspend { return token (ACE_SUSPEND); } +resume { return token (ACE_RESUME); } +remove { return token (ACE_REMOVE); } +stream { return token (ACE_USTREAM); } +Module { return token (ACE_MODULE_T); } +Service_Object { return token (ACE_SVC_OBJ_T); } +STREAM { return token (ACE_STREAM_T); } +active { return token (ACE_ACTIVE); } +inactive { return token (ACE_INACTIVE); } +":" { return token (':'); } +"*" { return token ('*'); } +"(" { return token ('('); } +")" { return token (')'); } +"{" { return token ('{'); } +"}" { return token ('}'); } +{string} { + // Check for first type of string, i.e., + // "double quotes" delimited. + ACE_TCHAR *s = ACE_OS::strrchr (yytext, '"'); + if (s == 0) + // Check for second type of string, i.e., + // 'single quotes' delimited. + s = ACE_OS::strrchr (yytext, '\''); + + ACE_ASSERT (s != 0); + // Eliminate the opening and closing double or + // single quotes. + *s = '\0'; + yyleng -= 1; + yylval->ident_ = ACE_SVC_CONF_PARAM->obstack.copy (yytext + 1, yyleng); + return token (ACE_STRING); } +{ident} { + yylval->ident_ = ACE_SVC_CONF_PARAM->obstack.copy (yytext, yyleng); + return token (ACE_IDENT); + } +{pathname} { + yylval->ident_ = ACE_SVC_CONF_PARAM->obstack.copy (yytext, yyleng); + return token (ACE_PATHNAME); + } +{white_space}+ ; /* EMPTY */ +{newline} { ACE_SVC_CONF_PARAM->yylineno++; ace_yylineno++; } +{other} { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("unknown character = (%d"), + *yytext)); + if (ACE_OS::ace_isprint (*yytext)) + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("|%c"), *yytext)); + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT (")\n"))); + } +<> { yyterminate(); } +%% +int +yywrap (void) +{ + ::fflush (yyin); + yytext[0] = '#'; + yyleng = 0; + + return 1; +} + +void +yy_push_buffer (FILE *file, yy_buffer_state *&buffer) +{ + // External synchronization is required. + + if (buffer == 0) + buffer = yy_create_buffer (file, YY_BUF_SIZE); + + yy_switch_to_buffer (buffer); +} + +void +yy_push_buffer (const ACE_TCHAR *directive, yy_buffer_state *&buffer) +{ + // External synchronization is required. + + // yyparse() may invoke yylex() multiple times when parsing + // a single directive. Prevent a new buffer from created during + // each call to yylex(). + if (YY_CURRENT_BUFFER != 0 + && directive == YY_CURRENT_BUFFER->yy_ch_buf) + return; + + if (buffer == 0) + { + // yy_scan_string() already switches the buffer so there is + // no need to explicitly make the switch. + buffer = yy_scan_string (directive); + } + else + yy_switch_to_buffer (buffer); +} + +void +yy_pop_buffer (yy_buffer_state *buffer) +{ + // External synchronization is required. + + yy_switch_to_buffer (buffer); +} diff --git a/ace/Svcconf/Svc_Conf.y b/ace/Svcconf/Svc_Conf.y new file mode 100644 index 00000000000..326c9d9b442 --- /dev/null +++ b/ace/Svcconf/Svc_Conf.y @@ -0,0 +1,464 @@ +%{ +// $Id$ + +#include "ace/ARGV.h" +#include "ace/Svc_Conf.h" +#include "ace/Module.h" +#include "ace/Stream.h" + +ACE_RCSID (ace, + Svc_Conf_y, + "$Id$") + +// Prototypes. +static ACE_Module_Type *ace_get_module (ACE_Static_Node *str_rec, + ACE_Static_Node *svc_type); +static ACE_Module_Type *ace_get_module (ACE_Static_Node *str_rec, + const ACE_TCHAR *svc_name); + +#define YYDEBUG_LEXER_TEXT (yytext[yyleng] = '\0', yytext) + +// Force the pretty debugging code to compile. +// #define YYDEBUG 1 + +// Keeps track of the number of errors encountered so far. +int yyerrno = 0; + +%} + +%token ACE_DYNAMIC ACE_STATIC ACE_SUSPEND ACE_RESUME ACE_REMOVE ACE_USTREAM +%token ACE_MODULE_T ACE_STREAM_T ACE_SVC_OBJ_T ACE_ACTIVE ACE_INACTIVE +%token ACE_PATHNAME ACE_IDENT ACE_STRING + +%start svc_config_entries + +%type ACE_IDENT ACE_STRING ACE_PATHNAME pathname parameters_opt +%type type status +%type dynamic static suspend resume remove module_list stream +%type stream_modules module svc_config_entry +%type stream_ops +%type svc_location +%type svc_initializer + +// Generate a pure (reentrant) parser -- GNU Bison only +%pure_parser + +%% + +svc_config_entries + : svc_config_entries svc_config_entry + { + if ($2 != 0) + { + $2->apply (); delete $2; + } + ACE_SVC_CONF_PARAM->obstack.release (); + } + | svc_config_entries error + { + ACE_SVC_CONF_PARAM->obstack.release (); + } + | /* EMPTY */ + ; + +svc_config_entry + : dynamic + | static + | suspend + | resume + | remove + | stream + ; + +dynamic + : ACE_DYNAMIC svc_location parameters_opt + { + if ($2 != 0) + $$ = new ACE_Dynamic_Node ($2, $3); + else + $$ = 0; + } + ; + +static + : ACE_STATIC ACE_IDENT parameters_opt + { + $$ = new ACE_Static_Node ($2, $3); + } + ; + +suspend + : ACE_SUSPEND ACE_IDENT + { + $$ = new ACE_Suspend_Node ($2); + } + ; + +resume + : ACE_RESUME ACE_IDENT + { + $$ = new ACE_Resume_Node ($2); + } + ; + +remove + : ACE_REMOVE ACE_IDENT + { + $$ = new ACE_Remove_Node ($2); + } + ; + +stream + : ACE_USTREAM stream_ops stream_modules + { + $$ = new ACE_Stream_Node ($2, $3); + } + | ACE_USTREAM ACE_IDENT { $$ = new ACE_Static_Node ($2); } stream_modules + { + $$ = new ACE_Dummy_Node ($3, $4); + } + ; + +stream_ops + : dynamic + { + } + | static + { + } + ; + +stream_modules + : '{' + { + // Initialize left context... + $$ = $0; + } + module_list '}' + { + $$ = $3; + } + | /* EMPTY */ { $$ = 0; } + ; + +module_list + : module_list module + { + if ($2 != 0) + { + $2->link ($1); + $$ = $2; + } + } + | /* EMPTY */ { $$ = 0; } + ; + +module + : dynamic + { + ACE_Static_Node *svc_type = $1; + + if (svc_type != 0) + { + ACE_Static_Node *module = $-1; + + ACE_ARGV args (svc_type->parameters ()); + ACE_Module_Type *mt = ace_get_module (module, + svc_type); + ACE_Stream_Type *st = + ACE_dynamic_cast (ACE_Stream_Type *, + ACE_const_cast (ACE_Service_Type_Impl *, + module->record ()->type ())); + + if (mt->init (args.argc (), args.argv ()) == -1 + || st->push (mt) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("dynamic initialization failed for Module %s\n"), + svc_type->name ())); + ACE_SVC_CONF_PARAM->yyerrno++; + } + } + } + | static + { + ACE_Module_Type *mt = ace_get_module ($-1, $1->name ()); + + if (((ACE_Stream_Type *) ($-1)->record ()->type ())->push (mt) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("Problem with static\n"))); + ACE_SVC_CONF_PARAM->yyerrno++; + } + } + | suspend + { + ACE_Module_Type *mt = ace_get_module ($-1, + $1->name ()); + if (mt != 0) + mt->suspend (); + } + | resume + { + ACE_Module_Type *mt = ace_get_module ($-1, + $1->name ()); + if (mt != 0) + mt->resume (); + } + | remove + { + ACE_Static_Node *stream = $-1; + ACE_Static_Node *module = $1; + ACE_Module_Type *mt = ace_get_module (stream, + module->name ()); + + ACE_Stream_Type *st = + ACE_dynamic_cast (ACE_Stream_Type *, + ACE_const_cast (ACE_Service_Type_Impl *, + stream->record ()->type ())); + if (mt != 0 && st->remove (mt) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("cannot remove Module_Type %s from STREAM_Type %s\n"), + module->name (), + stream->name ())); + ACE_SVC_CONF_PARAM->yyerrno++; + } + } + ; + +svc_location + : ACE_IDENT type svc_initializer status + { + u_int flags + = ACE_Service_Type::DELETE_THIS + | ($3->dispose () == 0 ? 0 : ACE_Service_Type::DELETE_OBJ); + ACE_Service_Object_Exterminator gobbler = 0; + void *sym = $3->symbol (&gobbler); + + if (sym != 0) + { + ACE_Service_Type_Impl *stp + = ace_create_service_type ($1, + $2, + sym, + flags, + gobbler); + $$ = new ACE_Service_Type ($1, + stp, + $3->handle (), + $4); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("Unable to find service: %s\n"), + $1)); + ++ACE_SVC_CONF_PARAM->yyerrno; + $$ = 0; + } + delete $3; + } + ; + +status + : ACE_ACTIVE + { + $$ = 1; + } + | ACE_INACTIVE + { + $$ = 0; + } + | /* EMPTY */ + { + $$ = 1; + } + ; + +svc_initializer + : pathname ':' ACE_IDENT + { + $$ = new ACE_Object_Node ($1, $3); + } + | pathname ':' ACE_IDENT '(' ')' + { + $$ = new ACE_Function_Node ($1, $3); + } + | ':' ACE_IDENT '(' ')' + { + $$ = new ACE_Static_Function_Node ($2); + } + ; + +type + : ACE_MODULE_T '*' + { + $$ = ACE_MODULE_T; + } + | ACE_SVC_OBJ_T '*' + { + $$ = ACE_SVC_OBJ_T; + } + | ACE_STREAM_T '*' + { + $$ = ACE_STREAM_T; + } + ; + +parameters_opt + : ACE_STRING + | /* EMPTY */ { $$ = 0; } + ; + +pathname + : ACE_PATHNAME + | ACE_IDENT + | ACE_STRING + ; + +%% +// Prints the error string to standard output. Cleans up the error +// messages. + +void +yyerror (const ACE_TCHAR *s) +{ +#if defined (ACE_NLOGGING) + ACE_UNUSED_ARG (s); +#endif /* ACE_NLOGGING */ + + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("[error %d] on line %d: %s\n"), + yyerrno, + yylineno, + s)); +} + +// Note that SRC_REC represents left context, which is the STREAM * +// record. + +static ACE_Module_Type * +ace_get_module (ACE_Static_Node *str_rec, + const ACE_TCHAR *svc_name) +{ + const ACE_Service_Type *sr = str_rec->record (); + const ACE_Service_Type_Impl *type = sr->type (); + ACE_Stream_Type *st = sr == 0 + ? 0 + : ACE_dynamic_cast (ACE_Stream_Type *, + ACE_const_cast (ACE_Service_Type_Impl *, + type)); + ACE_Module_Type *mt = st == 0 ? 0 : st->find (svc_name); + + if (sr == 0 || st == 0 || mt == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("cannot locate Module_Type %s in STREAM_Type %s\n"), + svc_name, + str_rec->name ())); + yyerrno++; + } + + return mt; +} + +static ACE_Module_Type * +ace_get_module (ACE_Static_Node *str_rec, + ACE_Static_Node *svc_type) +{ + const ACE_Service_Type *sr = str_rec->record (); + const ACE_Service_Type_Impl *type = sr->type (); + ACE_Stream_Type *st = sr == 0 ? 0 : (ACE_Stream_Type *) type; + const ACE_Service_Type *sv = svc_type->record (); + type = sv->type (); + ACE_Module_Type *mt = (ACE_Module_Type *) type; + const ACE_TCHAR *module_type_name = svc_type->name (); + + if (sr == 0 || st == 0 || mt == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("cannot locate Module_Type %s or STREAM_Type %s\n"), + module_type_name, + str_rec->name ())); + yyerrno++; + } + + // Make sure that the Module has the same name as the + // Module_Type object from the svc.conf file. + ACE_Module *mp = (ACE_Module *) mt->object (); + + if (ACE_OS::strcmp (mp->name (), module_type_name) != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("warning: assigning Module_Type name %s to Module %s since names differ\n"), + module_type_name, + mp->name ())); + mp->name (module_type_name); + } + + return mt; +} + +ACE_Service_Type_Impl * +ace_create_service_type (const ACE_TCHAR *name, + int type, + void *symbol, + u_int flags, + ACE_Service_Object_Exterminator gobbler) +{ + ACE_Service_Type_Impl *stp = 0; + + // Note, the only place we need to put a case statement. This is + // also the place where we'd put the RTTI tests, if the compiler + // actually supported them! + + switch (type) + { + case ACE_SVC_OBJ_T: + ACE_NEW_RETURN (stp, + ACE_Service_Object_Type ((ACE_Service_Object *) symbol, + name, flags, + gobbler), + 0); + break; + case ACE_MODULE_T: + ACE_NEW_RETURN (stp, + ACE_Module_Type (symbol, name, flags), + 0); + break; + case ACE_STREAM_T: + ACE_NEW_RETURN (stp, + ACE_Stream_Type (symbol, name, flags), + 0); + break; + default: + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("unknown case\n"))); + yyerrno++; + break; + } + return stp; +} + +#if defined (DEBUGGING) +// Current line number. +int yylineno = 1; + +// Name given on the command-line to envoke the program. +ACE_TCHAR *program_name; + +// Main driver program. + +int +main (int argc, char *argv[]) +{ + ACE_Svc_Conf_Param param (stdin); + + // Try to reopen any filename argument to use YYIN. + if (argc > 1 && (yyin = freopen (argv[1], "r", stdin)) == 0) + (void) ::fprintf (stderr, "usage: %s [file]\n", argv[0]), ACE_OS::exit (1); + + return yyparse (¶m); +} +#endif /* DEBUGGING */ diff --git a/ace/Svcconf/Svc_Conf_Lexer_Guard.cpp b/ace/Svcconf/Svc_Conf_Lexer_Guard.cpp new file mode 100644 index 00000000000..13848feaac9 --- /dev/null +++ b/ace/Svcconf/Svc_Conf_Lexer_Guard.cpp @@ -0,0 +1,36 @@ +// -*- C++ -*- + +#include "ace/Svc_Conf.h" + +#include "ace/Svc_Conf_Lexer_Guard.h" + +ACE_RCSID (ace, + Svc_Conf_Lexer_Guard, + "$Id$") + +ACE_Svc_Conf_Lexer_Guard::ACE_Svc_Conf_Lexer_Guard (ACE_Svc_Conf_Param *param) + : buffer_ (0) +{ + // External synchronization is required. + + // Note that allocation/deallocation is done once during the + // processing of a service configurator directive. Memory + // managements is done at a higher level, not in this class. This + // is necessary to prevent an allocation/deallocation from occurring + // when parsing/scanning each token. + + if (param->type == ACE_Svc_Conf_Param::SVC_CONF_FILE) + ::ace_yy_push_buffer (param->source.file, param->buffer); + else + ::ace_yy_push_buffer (param->source.directive, + param->buffer); + + this->buffer_ = param->buffer; +} + +ACE_Svc_Conf_Lexer_Guard::~ACE_Svc_Conf_Lexer_Guard (void) +{ + // External synchronization is required. + + ::ace_yy_pop_buffer (this->buffer_); +} diff --git a/ace/Svcconf/Svc_Conf_Lexer_Guard.h b/ace/Svcconf/Svc_Conf_Lexer_Guard.h new file mode 100644 index 00000000000..4e68bb60ed5 --- /dev/null +++ b/ace/Svcconf/Svc_Conf_Lexer_Guard.h @@ -0,0 +1,79 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Svc_Conf_Lexer_Guard.h + * + * $Id$ + * + * @author Ossama Othman + */ +//============================================================================= + +#ifndef ACE_SVC_CONF_LEXER_GUARD_H +#define ACE_SVC_CONF_LEXER_GUARD_H + +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/// Forward declarations +class ACE_Svc_Conf_Param; + +struct ace_yy_buffer_state; + +/** + * @class ACE_Svc_Conf_Lexer_Guard + * + * @brief "Guard" that ensures lexer buffer switching is + * exception-safe. + * + * Buffers are switched, if necessary, each time a token is + * parsed/scanned. The buffer switching must be synchronized + * externally. This class performs no synchronization. + * + * @note Note that allocation/deallocation is done once during the + * processing of a service configurator directive. Memory + * managements is done at a higher level, not in this class. + * This is necessary to prevent an allocation/deallocation from + * occurring when parsing/scanning each token. + */ +class ACE_Svc_Conf_Lexer_Guard +{ +public: + + /// Constructor + /** + * Switches buffers, if necessary, when token scanning first + * begins. Allocation of the buffer will also occur if one has not + * already been allocated. This operation effectively pushes a + * buffer on to a stack. + */ + ACE_Svc_Conf_Lexer_Guard (ACE_Svc_Conf_Param *param); + + /// Destructor + /** + * Switches buffers, if necessary when token scanning completes. No + * buffer deallocation occurs here. Buffers are deallocated when + * parsing of the entire directive is done, not when scanning of a + * single token is done. This operation effective pops a buffer off + * of a stack. + */ + ~ACE_Svc_Conf_Lexer_Guard (void); + +private: + + /// Lexer buffer that corresponds to the current Service + /// Configurator file/direct scan. + ace_yy_buffer_state *buffer_; + +}; + + +#include "ace/post.h" + +#endif /* ACE_SVC_CONF_LEXER_GUARD_H */ diff --git a/ace/Svcconf/Svc_Conf_Tokens.h b/ace/Svcconf/Svc_Conf_Tokens.h new file mode 100644 index 00000000000..14a41622c1d --- /dev/null +++ b/ace/Svcconf/Svc_Conf_Tokens.h @@ -0,0 +1,16 @@ +// $Id$ +#define ACE_DYNAMIC 257 +#define ACE_STATIC 258 +#define ACE_SUSPEND 259 +#define ACE_RESUME 260 +#define ACE_REMOVE 261 +#define ACE_USTREAM 262 +#define ACE_MODULE_T 263 +#define ACE_STREAM_T 264 +#define ACE_SVC_OBJ_T 265 +#define ACE_ACTIVE 266 +#define ACE_INACTIVE 267 +#define ACE_PATHNAME 268 +#define ACE_IDENT 269 +#define ACE_STRING 270 + diff --git a/ace/Svcconf/Svc_Conf_l.cpp b/ace/Svcconf/Svc_Conf_l.cpp new file mode 100644 index 00000000000..be53770e146 --- /dev/null +++ b/ace/Svcconf/Svc_Conf_l.cpp @@ -0,0 +1,1841 @@ +#define ACE_YY_NO_UNPUT +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header$ + */ + +#define FLEX_SCANNER +#define ACE_YY_FLEX_MAJOR_VERSION 2 +#define ACE_YY_FLEX_MINOR_VERSION 5 + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include "ace/OS.h" +#include "ace/Object_Manager.h" + +/* Use prototypes in function declarations. */ +#define ACE_YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define ACE_YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define ACE_YY_USE_PROTOS +#define ACE_YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include /**/ +#include /**/ +#define ACE_YY_USE_CONST +#define ACE_YY_USE_PROTOS +#endif + +#ifdef ACE_YY_USE_CONST +#define ace_yyconst const +#else +#define ace_yyconst +#endif + + +#ifdef ACE_YY_USE_PROTOS +#define ACE_YY_PROTO(proto) proto +#else +#define ACE_YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define ACE_YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define ACE_YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN ace_yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The ACE_YYSTATE alias is for lex + * compatibility. + */ +#define ACE_YY_START ((ace_yy_start - 1) / 2) +#define ACE_YYSTATE ACE_YY_START + +/* Action number for EOF rule of a given start state. */ +#define ACE_YY_STATE_EOF(state) (ACE_YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define ACE_YY_NEW_FILE ace_yyrestart( ace_yyin ) + +#define ACE_YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define ACE_YY_BUF_SIZE 16384 + +typedef struct ace_yy_buffer_state *ACE_YY_BUFFER_STATE; + +extern int ace_yyleng; +extern FILE *ace_yyin, *ace_yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * ace_yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the ace_yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define ace_yyless(n) \ + do \ + { \ + /* Undo effects of setting up ace_yytext. */ \ + *ace_yy_cp = ace_yy_hold_char; \ + ACE_YY_RESTORE_ACE_YY_MORE_OFFSET \ + ace_yy_c_buf_p = ace_yy_cp = ace_yy_bp + n - ACE_YY_MORE_ADJ; \ + ACE_YY_DO_BEFORE_ACTION; /* set up ace_yytext again */ \ + } \ + while ( 0 ) + +#if 0 +#define unput(c) ace_yyunput( c, ace_yytext_ptr ) +#endif /* 0 */ + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int ace_yy_size_t; + + +struct ace_yy_buffer_state + { + FILE *ace_yy_input_file; + + ACE_TCHAR *ace_yy_ch_buf; /* input buffer */ + ACE_TCHAR *ace_yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + ace_yy_size_t ace_yy_buf_size; + + /* Number of characters read into ace_yy_ch_buf, not including EOB + * characters. + */ + int ace_yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int ace_yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int ace_yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int ace_yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int ace_yy_fill_buffer; + + int ace_yy_buffer_status; +#define ACE_YY_BUFFER_NEW 0 +#define ACE_YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as ACE_YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via ace_yyrestart()), so that the user can continue scanning by + * just pointing ace_yyin at a new input file. + */ +#define ACE_YY_BUFFER_EOF_PENDING 2 + }; + +static ACE_YY_BUFFER_STATE ace_yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define ACE_YY_CURRENT_BUFFER ace_yy_current_buffer + + +/* ace_yy_hold_char holds the character lost when ace_yytext is formed. */ +static ACE_TCHAR ace_yy_hold_char; + +static int ace_yy_n_chars; /* number of characters read into ace_yy_ch_buf */ + + +int ace_yyleng; + +/* Points to current character in buffer. */ +static ACE_TCHAR *ace_yy_c_buf_p = (ACE_TCHAR *) 0; +static int ace_yy_init = 1; /* whether we need to initialize */ +static int ace_yy_start = 0; /* start state number */ + +/* Flag which is used to allow ace_yywrap()'s to do buffer switches + * instead of setting up a fresh ace_yyin. A bit of a hack ... + */ +static int ace_yy_did_buffer_switch_on_eof; + +void ace_yyrestart ACE_YY_PROTO(( FILE *input_file )); + +void ace_yy_switch_to_buffer ACE_YY_PROTO(( ACE_YY_BUFFER_STATE new_buffer )); +void ace_yy_load_buffer_state ACE_YY_PROTO(( void )); +ACE_YY_BUFFER_STATE ace_yy_create_buffer ACE_YY_PROTO(( FILE *file, int size )); +void ace_yy_delete_buffer ACE_YY_PROTO(( ACE_YY_BUFFER_STATE b )); +void ace_yy_init_buffer ACE_YY_PROTO(( ACE_YY_BUFFER_STATE b, FILE *file )); +void ace_yy_flush_buffer ACE_YY_PROTO(( ACE_YY_BUFFER_STATE b )); +#define ACE_YY_FLUSH_BUFFER ace_yy_flush_buffer( ace_yy_current_buffer ) + +ACE_YY_BUFFER_STATE ace_yy_scan_buffer ACE_YY_PROTO(( ACE_TCHAR *base, ace_yy_size_t size )); +ACE_YY_BUFFER_STATE ace_yy_scan_string ACE_YY_PROTO(( ace_yyconst ACE_TCHAR *ace_yy_str )); +ACE_YY_BUFFER_STATE ace_yy_scan_bytes ACE_YY_PROTO(( ace_yyconst ACE_TCHAR *bytes, int len )); + +static void *ace_yy_flex_alloc ACE_YY_PROTO(( ace_yy_size_t )); +static void *ace_yy_flex_realloc ACE_YY_PROTO(( void *, ace_yy_size_t )); +static void ace_yy_flex_free ACE_YY_PROTO(( void * )); + +#define ace_yy_new_buffer ace_yy_create_buffer + +#define ace_yy_set_interactive(is_interactive) \ + { \ + if ( ! ace_yy_current_buffer ) \ + ace_yy_current_buffer = ace_yy_create_buffer( ace_yyin, ACE_YY_BUF_SIZE ); \ + ace_yy_current_buffer->ace_yy_is_interactive = is_interactive; \ + } + +#define ace_yy_set_bol(at_bol) \ + { \ + if ( ! ace_yy_current_buffer ) \ + ace_yy_current_buffer = ace_yy_create_buffer( ace_yyin, ACE_YY_BUF_SIZE ); \ + ace_yy_current_buffer->ace_yy_at_bol = at_bol; \ + } + +#define ACE_YY_AT_BOL() (ace_yy_current_buffer->ace_yy_at_bol) + +typedef ACE_TCHAR ACE_YY_CHAR; +FILE *ace_yyin = (FILE *) 0, *ace_yyout = (FILE *) 0; +typedef int ace_yy_state_type; +extern ACE_TCHAR *ace_yytext; +#define ace_yytext_ptr ace_yytext + +static ace_yy_state_type ace_yy_get_previous_state ACE_YY_PROTO(( void )); +static ace_yy_state_type ace_yy_try_NUL_trans ACE_YY_PROTO(( ace_yy_state_type current_state )); +static int ace_yy_get_next_buffer ACE_YY_PROTO(( void )); +static void ace_yy_fatal_error ACE_YY_PROTO(( ace_yyconst ACE_TCHAR msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up ace_yytext. + */ +#define ACE_YY_DO_BEFORE_ACTION \ + ace_yytext_ptr = ace_yy_bp; \ + ace_yyleng = (int) (ace_yy_cp - ace_yy_bp); \ + ace_yy_hold_char = *ace_yy_cp; \ + *ace_yy_cp = '\0'; \ + ace_yy_c_buf_p = ace_yy_cp; + +#define ACE_YY_NUM_RULES 25 +#define ACE_YY_END_OF_BUFFER 26 +static ace_yyconst short int ace_yy_accept[107] = + { 0, + 0, 0, 0, 0, 0, 0, 26, 24, 22, 23, + 24, 21, 24, 15, 16, 14, 21, 13, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 17, 18, 24, + 22, 0, 19, 21, 0, 0, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 0, 1, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 8, 10, 20, 11, 20, 20, 6, 5, + 3, 7, 20, 20, 2, 20, 4, 20, 12, 20, + + 20, 20, 20, 20, 9, 0 + } ; + +static ace_yyconst int ace_yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 5, 6, 7, 5, 8, 5, 9, 10, + 11, 12, 5, 5, 13, 13, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 15, 5, 5, + 5, 5, 5, 5, 16, 17, 17, 17, 18, 17, + 17, 17, 17, 17, 17, 17, 19, 17, 20, 17, + 17, 21, 22, 23, 17, 17, 17, 17, 17, 17, + 5, 13, 5, 5, 24, 5, 25, 26, 27, 28, + + 29, 17, 17, 17, 30, 31, 17, 32, 33, 34, + 35, 36, 17, 37, 38, 39, 40, 41, 17, 17, + 42, 17, 43, 5, 44, 13, 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, 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, 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, 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 + } ; + +static ace_yyconst int ace_yy_meta[45] = + { 0, + 1, 1, 1, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2 + } ; + +static ace_yyconst short int ace_yy_base[112] = + { 0, + 0, 132, 0, 127, 0, 123, 129, 437, 43, 437, + 122, 112, 109, 437, 437, 437, 102, 437, 38, 41, + 42, 51, 54, 55, 64, 65, 74, 437, 437, 104, + 56, 93, 86, 70, 69, 66, 65, 75, 78, 87, + 88, 96, 97, 107, 108, 111, 125, 58, 437, 129, + 137, 141, 145, 149, 153, 157, 163, 171, 175, 181, + 187, 193, 199, 203, 207, 213, 217, 223, 233, 234, + 242, 252, 253, 261, 262, 265, 271, 274, 277, 285, + 289, 300, 301, 310, 311, 320, 323, 324, 333, 334, + 343, 344, 347, 353, 356, 359, 365, 371, 368, 374, + + 377, 386, 389, 390, 398, 437, 428, 49, 430, 45, + 433 + } ; + +static ace_yyconst short int ace_yy_def[112] = + { 0, + 106, 1, 1, 1, 1, 1, 106, 106, 106, 106, + 107, 108, 109, 106, 106, 106, 108, 106, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 106, 106, 111, + 106, 107, 107, 108, 108, 109, 109, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 111, 106, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + + 110, 110, 110, 110, 110, 0, 106, 106, 106, 106, + 106 + } ; + +static ace_yyconst short int ace_yy_nxt[482] = + { 0, + 8, 9, 10, 9, 8, 11, 8, 12, 13, 14, + 15, 16, 17, 17, 18, 19, 19, 19, 20, 19, + 19, 21, 19, 22, 23, 19, 19, 24, 19, 25, + 19, 19, 19, 19, 19, 19, 26, 27, 19, 19, + 19, 19, 28, 29, 31, 34, 31, 38, 34, 34, + 34, 34, 35, 34, 34, 35, 35, 31, 34, 31, + 49, 34, 34, 34, 40, 106, 34, 34, 35, 35, + 41, 34, 34, 37, 37, 39, 34, 34, 35, 35, + 42, 34, 34, 106, 106, 34, 34, 34, 35, 106, + 34, 33, 106, 45, 34, 34, 43, 44, 33, 34, + + 34, 106, 106, 34, 34, 50, 49, 51, 34, 34, + 106, 106, 46, 47, 34, 34, 106, 37, 34, 34, + 34, 106, 106, 34, 52, 106, 35, 33, 106, 30, + 54, 55, 34, 30, 53, 58, 34, 34, 30, 106, + 56, 34, 106, 106, 34, 57, 106, 59, 34, 34, + 106, 106, 34, 34, 62, 106, 34, 34, 106, 106, + 34, 34, 60, 106, 34, 34, 106, 106, 61, 34, + 34, 106, 106, 65, 64, 34, 106, 106, 34, 66, + 106, 63, 34, 34, 106, 106, 106, 34, 34, 106, + 106, 67, 106, 34, 34, 106, 106, 106, 106, 34, + + 34, 106, 68, 70, 106, 34, 34, 106, 73, 69, + 34, 34, 106, 106, 34, 34, 71, 106, 72, 34, + 34, 106, 106, 106, 34, 34, 106, 106, 74, 34, + 34, 106, 106, 106, 106, 34, 106, 106, 106, 76, + 34, 34, 106, 75, 106, 34, 34, 106, 106, 34, + 106, 77, 106, 106, 34, 79, 106, 78, 81, 34, + 34, 106, 80, 106, 34, 34, 106, 106, 34, 34, + 82, 84, 34, 34, 34, 106, 106, 34, 34, 106, + 83, 34, 106, 34, 34, 106, 34, 85, 106, 34, + 86, 106, 34, 106, 87, 106, 34, 34, 106, 106, + + 88, 34, 89, 106, 106, 90, 106, 34, 34, 106, + 106, 91, 34, 34, 106, 106, 106, 34, 34, 106, + 106, 92, 34, 34, 106, 106, 106, 34, 106, 106, + 34, 34, 34, 93, 106, 34, 34, 106, 106, 94, + 34, 34, 106, 106, 106, 34, 34, 106, 106, 95, + 34, 34, 106, 106, 34, 34, 34, 106, 106, 34, + 34, 106, 106, 34, 96, 34, 34, 106, 34, 106, + 106, 34, 34, 106, 97, 34, 98, 34, 34, 106, + 34, 34, 106, 34, 34, 106, 34, 99, 106, 34, + 100, 106, 106, 34, 106, 106, 34, 34, 34, 101, + + 106, 34, 34, 106, 106, 34, 106, 102, 106, 106, + 34, 106, 106, 106, 103, 104, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 105, 32, + 32, 36, 36, 48, 48, 48, 7, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106 + } ; + +static ace_yyconst short int ace_yy_chk[482] = + { 0, + 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, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 9, 19, 9, 110, 20, 21, + 19, 108, 19, 20, 21, 20, 21, 31, 22, 31, + 48, 23, 24, 22, 21, 22, 23, 24, 23, 24, + 21, 25, 26, 37, 36, 20, 25, 26, 25, 26, + 23, 27, 38, 35, 34, 39, 27, 38, 27, 38, + 39, 33, 39, 26, 40, 41, 24, 25, 32, 40, + + 41, 40, 41, 42, 43, 39, 30, 40, 42, 43, + 42, 43, 27, 27, 44, 45, 17, 13, 46, 44, + 45, 44, 45, 46, 41, 46, 12, 11, 7, 6, + 43, 44, 47, 4, 42, 46, 50, 47, 2, 47, + 45, 50, 0, 50, 51, 45, 0, 46, 52, 51, + 0, 51, 53, 52, 51, 52, 54, 53, 0, 53, + 55, 54, 47, 54, 56, 55, 0, 55, 50, 56, + 57, 56, 0, 54, 53, 57, 0, 57, 58, 55, + 0, 52, 59, 58, 0, 58, 0, 59, 60, 59, + 0, 56, 0, 60, 61, 60, 0, 0, 0, 61, + + 62, 61, 57, 59, 0, 62, 63, 62, 62, 58, + 64, 63, 0, 63, 65, 64, 60, 64, 61, 65, + 66, 65, 0, 0, 67, 66, 0, 66, 63, 67, + 68, 67, 0, 0, 0, 68, 0, 68, 0, 65, + 69, 70, 0, 64, 0, 69, 70, 69, 70, 71, + 0, 66, 0, 0, 71, 68, 71, 67, 70, 72, + 73, 0, 69, 0, 72, 73, 72, 73, 74, 75, + 71, 73, 76, 74, 75, 74, 75, 76, 77, 76, + 72, 78, 0, 77, 79, 77, 78, 74, 78, 79, + 75, 79, 80, 0, 76, 0, 81, 80, 0, 80, + + 77, 81, 78, 81, 0, 79, 0, 82, 83, 0, + 0, 80, 82, 83, 82, 83, 0, 84, 85, 0, + 0, 81, 84, 85, 84, 85, 0, 86, 0, 0, + 87, 88, 86, 82, 86, 87, 88, 87, 88, 85, + 89, 90, 0, 0, 0, 89, 90, 89, 90, 87, + 91, 92, 0, 0, 93, 91, 92, 91, 92, 93, + 94, 93, 0, 95, 88, 94, 96, 94, 95, 0, + 95, 96, 97, 96, 93, 99, 94, 97, 98, 97, + 99, 100, 99, 98, 101, 98, 100, 96, 100, 101, + 98, 101, 0, 102, 0, 0, 103, 104, 102, 100, + + 102, 103, 104, 103, 104, 105, 0, 101, 0, 0, + 105, 0, 105, 0, 102, 103, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 107, + 107, 109, 109, 111, 111, 111, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106 + } ; + +static ace_yy_state_type ace_yy_last_accepting_state; +static ACE_TCHAR *ace_yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define ace_yymore() ace_yymore_used_but_not_detected +#define ACE_YY_MORE_ADJ 0 +#define ACE_YY_RESTORE_ACE_YY_MORE_OFFSET +ACE_TCHAR *ace_yytext; +#define INITIAL 0 +// $Id$ +// Sample lexical analysis for regular expression subset. Must be +// compiled with FLEX and an ANSI C++ compiler. + +// Lexical tokens values defined by YACC. +#include "ace/Svc_Conf.h" +#include "ace/Svc_Conf_Tokens.h" +#include "ace/Svc_Conf_Lexer_Guard.h" + +ACE_RCSID (ace, + Svc_Conf_l, + "$Id$") + +// Keeps track of the current line for debugging output. +int ace_yylineno = 1; + +#define token(x) x +#define PARAMETERS 1 + +#define NORMAL 2 + + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef ACE_YY_SKIP_ACE_YYWRAP +#ifdef __cplusplus +extern "C" int ace_yywrap ACE_YY_PROTO(( void )); +#else +extern int ace_yywrap ACE_YY_PROTO(( void )); +#endif +#endif + +#ifndef ACE_YY_NO_UNPUT +static void ace_yyunput ACE_YY_PROTO(( int c, ACE_TCHAR *buf_ptr )); +#endif + +#ifndef ace_yytext_ptr +static void ace_yy_flex_strncpy ACE_YY_PROTO(( ACE_TCHAR *, ace_yyconst ACE_TCHAR *, int )); +#endif + +#ifdef ACE_YY_NEED_STRLEN +static int ace_yy_flex_strlen ACE_YY_PROTO(( ace_yyconst ACE_TCHAR * )); +#endif + +#ifndef ACE_YY_NO_INPUT +#ifdef __cplusplus +static int ace_yyinput ACE_YY_PROTO(( void )); +#else +static int input ACE_YY_PROTO(( void )); +#endif +#endif + +#if ACE_YY_STACK_USED +static int ace_yy_start_stack_ptr = 0; +static int ace_yy_start_stack_depth = 0; +static int *ace_yy_start_stack = 0; +#ifndef ACE_YY_NO_PUSH_STATE +static void ace_yy_push_state ACE_YY_PROTO(( int new_state )); +#endif +#ifndef ACE_YY_NO_POP_STATE +static void ace_yy_pop_state ACE_YY_PROTO(( void )); +#endif +#ifndef ACE_YY_NO_TOP_STATE +static int ace_yy_top_state ACE_YY_PROTO(( void )); +#endif + +#else +#define ACE_YY_NO_PUSH_STATE 1 +#define ACE_YY_NO_POP_STATE 1 +#define ACE_YY_NO_TOP_STATE 1 +#endif + +#ifdef ACE_YY_MALLOC_DECL +ACE_YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include /**/ +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef ACE_YY_READ_BUF_SIZE +#define ACE_YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ACE_SVC_CONF_ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ACE_SVC_CONF_ECHO (void) fwrite( ace_yytext, ace_yyleng, 1, ace_yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or ACE_YY_NULL, + * is returned in "result". + */ +#ifndef ACE_YY_INPUT +#define ACE_YY_INPUT(buf,result,max_size) \ + if ( ace_yy_current_buffer->ace_yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( ace_yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (ACE_TCHAR) c; \ + if ( c == '\n' ) \ + buf[n++] = (ACE_TCHAR) c; \ + if ( c == EOF && ferror( ace_yyin ) ) \ + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "input in flex scanner failed") ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, sizeof (ACE_TCHAR), max_size, ace_yyin )) == 0) \ + && ferror( ace_yyin ) ) \ + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT("input in flex scanner failed") ); +#endif + +/* No semi-colon after return; correct usage is to write "ace_yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef ace_yyterminate +#define ace_yyterminate() return ACE_YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef ACE_YY_START_STACK_INCR +#define ACE_YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef ACE_YY_FATAL_ERROR +#define ACE_YY_FATAL_ERROR(msg) ace_yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef ACE_YY_DECL +#define ACE_YY_DECL int ace_yylex ACE_YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after ace_yytext and ace_yyleng + * have been set up. + */ +#ifndef ACE_YY_USER_ACTION +#define ACE_YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef ACE_YY_BREAK +#define ACE_YY_BREAK break; +#endif + +#define ACE_YY_RULE_SETUP \ + if ( ace_yyleng > 0 ) \ + ace_yy_current_buffer->ace_yy_at_bol = \ + (ace_yytext[ace_yyleng - 1] == '\n'); \ + ACE_YY_USER_ACTION + +ACE_YY_DECL + { + ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, + ace_mon, + *ACE_Static_Object_Lock::instance (), + -1)); + + ACE_Svc_Conf_Lexer_Guard ace_lexer_guard (ACE_SVC_CONF_PARAM); + + register ace_yy_state_type ace_yy_current_state; + register ACE_TCHAR *ace_yy_cp=0, *ace_yy_bp=0; + register int ace_yy_act; + + + + + if ( ace_yy_init ) + { + ace_yy_init = 0; + +#ifdef ACE_YY_USER_INIT + ACE_YY_USER_INIT; +#endif + + if ( ! ace_yy_start ) + ace_yy_start = 1; /* first start state */ + + if ( ! ace_yyin ) + ace_yyin = stdin; + + if ( ! ace_yyout ) + ace_yyout = stdout; + + if ( ! ace_yy_current_buffer ) + ace_yy_current_buffer = + ace_yy_create_buffer( ace_yyin, ACE_YY_BUF_SIZE ); + + ace_yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + ace_yy_cp = ace_yy_c_buf_p; + + /* Support of ace_yytext. */ + *ace_yy_cp = ace_yy_hold_char; + + /* ace_yy_bp points to the position in ace_yy_ch_buf of the start of + * the current run. + */ + ace_yy_bp = ace_yy_cp; + + ace_yy_current_state = ace_yy_start; + ace_yy_current_state += ACE_YY_AT_BOL(); +ace_yy_match: + do + { + register ACE_YY_CHAR ace_yy_c = ace_yy_ec[ACE_YY_SC_TO_UI(*ace_yy_cp)]; + if ( ace_yy_accept[ace_yy_current_state] ) + { + ace_yy_last_accepting_state = ace_yy_current_state; + ace_yy_last_accepting_cpos = ace_yy_cp; + } + while ( ace_yy_chk[ace_yy_base[ace_yy_current_state] + ace_yy_c] != ace_yy_current_state ) + { + ace_yy_current_state = (int) ace_yy_def[ace_yy_current_state]; + if ( ace_yy_current_state >= 107 ) + ace_yy_c = ace_yy_meta[(unsigned int) ace_yy_c]; + } + ace_yy_current_state = ace_yy_nxt[ace_yy_base[ace_yy_current_state] + (unsigned int) ace_yy_c]; + ++ace_yy_cp; + } + while ( ace_yy_base[ace_yy_current_state] != 437 ); + +ace_yy_find_action: + ace_yy_act = ace_yy_accept[ace_yy_current_state]; + if ( ace_yy_act == 0 ) + { /* have to back up */ + ace_yy_cp = ace_yy_last_accepting_cpos; + ace_yy_current_state = ace_yy_last_accepting_state; + ace_yy_act = ace_yy_accept[ace_yy_current_state]; + } + + ACE_YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( ace_yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of ACE_YY_DO_BEFORE_ACTION */ + *ace_yy_cp = ace_yy_hold_char; + ace_yy_cp = ace_yy_last_accepting_cpos; + ace_yy_current_state = ace_yy_last_accepting_state; + goto ace_yy_find_action; + +case 1: +*ace_yy_cp = ace_yy_hold_char; /* undo effects of setting up ace_yytext */ +ace_yy_c_buf_p = ace_yy_cp -= 1; +ACE_YY_DO_BEFORE_ACTION; /* set up ace_yytext again */ +ACE_YY_RULE_SETUP +; /* EMPTY */ + ACE_YY_BREAK +case 2: +ACE_YY_RULE_SETUP +{ return token (ACE_DYNAMIC); } + // ACE_YY_BREAK +case 3: +ACE_YY_RULE_SETUP +{ return token (ACE_STATIC); } + // ACE_YY_BREAK +case 4: +ACE_YY_RULE_SETUP +{ return token (ACE_SUSPEND); } + // ACE_YY_BREAK +case 5: +ACE_YY_RULE_SETUP +{ return token (ACE_RESUME); } + // ACE_YY_BREAK +case 6: +ACE_YY_RULE_SETUP +{ return token (ACE_REMOVE); } + // ACE_YY_BREAK +case 7: +ACE_YY_RULE_SETUP +{ return token (ACE_USTREAM); } + // ACE_YY_BREAK +case 8: +ACE_YY_RULE_SETUP +{ return token (ACE_MODULE_T); } + // ACE_YY_BREAK +case 9: +ACE_YY_RULE_SETUP +{ return token (ACE_SVC_OBJ_T); } + // ACE_YY_BREAK +case 10: +ACE_YY_RULE_SETUP +{ return token (ACE_STREAM_T); } + // ACE_YY_BREAK +case 11: +ACE_YY_RULE_SETUP +{ return token (ACE_ACTIVE); } + // ACE_YY_BREAK +case 12: +ACE_YY_RULE_SETUP +{ return token (ACE_INACTIVE); } + // ACE_YY_BREAK +case 13: +ACE_YY_RULE_SETUP +{ return token (':'); } + // ACE_YY_BREAK +case 14: +ACE_YY_RULE_SETUP +{ return token ('*'); } + // ACE_YY_BREAK +case 15: +ACE_YY_RULE_SETUP +{ return token ('('); } + // ACE_YY_BREAK +case 16: +ACE_YY_RULE_SETUP +{ return token (')'); } + // ACE_YY_BREAK +case 17: +ACE_YY_RULE_SETUP +{ return token ('{'); } + // ACE_YY_BREAK +case 18: +ACE_YY_RULE_SETUP +{ return token ('}'); } + // ACE_YY_BREAK +case 19: +ACE_YY_RULE_SETUP +{ + // Check for first type of string, i.e., + // "double quotes" delimited. + ACE_TCHAR *s = ACE_OS::strrchr (ace_yytext, '"'); + if (s == 0) + // Check for second type of string, i.e., + // 'single quotes' delimited. + s = ACE_OS::strrchr (ace_yytext, '\''); + + ACE_ASSERT (s != 0); + // Eliminate the opening and closing double or + // single quotes. + *s = '\0'; + ace_yyleng -= 1; + ace_yylval->ident_ = ACE_SVC_CONF_PARAM->obstack.copy (ace_yytext + 1, ace_yyleng); + return token (ACE_STRING); } + // ACE_YY_BREAK +case 20: +ACE_YY_RULE_SETUP +{ + ace_yylval->ident_ = ACE_SVC_CONF_PARAM->obstack.copy (ace_yytext, ace_yyleng); + return token (ACE_IDENT); + } + // ACE_YY_BREAK +case 21: +ACE_YY_RULE_SETUP +{ + ace_yylval->ident_ = ACE_SVC_CONF_PARAM->obstack.copy (ace_yytext, ace_yyleng); + return token (ACE_PATHNAME); + } + // ACE_YY_BREAK +case 22: +ACE_YY_RULE_SETUP +; /* EMPTY */ + ACE_YY_BREAK +case 23: +ACE_YY_RULE_SETUP +{ ACE_SVC_CONF_PARAM->yylineno++; ace_yylineno++; } + ACE_YY_BREAK +case 24: +ACE_YY_RULE_SETUP +{ + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("unknown character = (%d"), + *ace_yytext)); + if (ACE_OS::ace_isprint (*ace_yytext)) + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("|%c"), *ace_yytext)); + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT (")\n"))); + } + ACE_YY_BREAK +case ACE_YY_STATE_EOF(INITIAL): +case ACE_YY_STATE_EOF(PARAMETERS): +case ACE_YY_STATE_EOF(NORMAL): +{ ace_yyterminate(); } +// ACE_YY_BREAK +case 25: +ACE_YY_RULE_SETUP +ACE_SVC_CONF_ECHO; + ACE_YY_BREAK + + case ACE_YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB ACE_TCHAR. */ + int ace_yy_amount_of_matched_text = (int) (ace_yy_cp - ace_yytext_ptr) - 1; + + /* Undo the effects of ACE_YY_DO_BEFORE_ACTION. */ + *ace_yy_cp = ace_yy_hold_char; + ACE_YY_RESTORE_ACE_YY_MORE_OFFSET + + if ( ace_yy_current_buffer->ace_yy_buffer_status == ACE_YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed ace_yyin at a new source and called + * ace_yylex(). If so, then we have to assure + * consistency between ace_yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + ace_yy_n_chars = ace_yy_current_buffer->ace_yy_n_chars; + ace_yy_current_buffer->ace_yy_input_file = ace_yyin; + ace_yy_current_buffer->ace_yy_buffer_status = ACE_YY_BUFFER_NORMAL; + } + + /* Note that here we test for ace_yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since ace_yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( ace_yy_c_buf_p <= &ace_yy_current_buffer->ace_yy_ch_buf[ace_yy_n_chars] ) + { /* This was really a NUL. */ + ace_yy_state_type ace_yy_next_state; + + ace_yy_c_buf_p = ace_yytext_ptr + ace_yy_amount_of_matched_text; + + ace_yy_current_state = ace_yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * ace_yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + ace_yy_next_state = ace_yy_try_NUL_trans( ace_yy_current_state ); + + ace_yy_bp = ace_yytext_ptr + ACE_YY_MORE_ADJ; + + if ( ace_yy_next_state ) + { + /* Consume the NUL. */ + ace_yy_cp = ++ace_yy_c_buf_p; + ace_yy_current_state = ace_yy_next_state; + goto ace_yy_match; + } + + else + { + ace_yy_cp = ace_yy_c_buf_p; + goto ace_yy_find_action; + } + } + + else switch ( ace_yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + ace_yy_did_buffer_switch_on_eof = 0; + + if ( ace_yywrap() ) + { + /* Note: because we've taken care in + * ace_yy_get_next_buffer() to have set up + * ace_yytext, we can now set up + * ace_yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * ACE_YY_NULL, it'll still work - another + * ACE_YY_NULL will get returned. + */ + ace_yy_c_buf_p = ace_yytext_ptr + ACE_YY_MORE_ADJ; + + ace_yy_act = ACE_YY_STATE_EOF(ACE_YY_START); + goto do_action; + } + + else + { + if ( ! ace_yy_did_buffer_switch_on_eof ) + ACE_YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + ace_yy_c_buf_p = + ace_yytext_ptr + ace_yy_amount_of_matched_text; + + ace_yy_current_state = ace_yy_get_previous_state(); + + ace_yy_cp = ace_yy_c_buf_p; + ace_yy_bp = ace_yytext_ptr + ACE_YY_MORE_ADJ; + goto ace_yy_match; + + case EOB_ACT_LAST_MATCH: + ace_yy_c_buf_p = + &ace_yy_current_buffer->ace_yy_ch_buf[ace_yy_n_chars]; + + ace_yy_current_state = ace_yy_get_previous_state(); + + ace_yy_cp = ace_yy_c_buf_p; + ace_yy_bp = ace_yytext_ptr + ACE_YY_MORE_ADJ; + goto ace_yy_find_action; + } + break; + } + + default: + ACE_YY_FATAL_ERROR( + ACE_LIB_TEXT("fatal flex scanner internal error--no action found") ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of ace_yylex */ + + +/* ace_yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int ace_yy_get_next_buffer() + { + register ACE_TCHAR *dest = ace_yy_current_buffer->ace_yy_ch_buf; + register ACE_TCHAR *source = ace_yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( ace_yy_c_buf_p > &ace_yy_current_buffer->ace_yy_ch_buf[ace_yy_n_chars + 1] ) + ACE_YY_FATAL_ERROR( + ACE_LIB_TEXT("fatal flex scanner internal error--end of buffer missed") ); + + if ( ace_yy_current_buffer->ace_yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( ace_yy_c_buf_p - ace_yytext_ptr - ACE_YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (ace_yy_c_buf_p - ace_yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( ace_yy_current_buffer->ace_yy_buffer_status == ACE_YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + ace_yy_current_buffer->ace_yy_n_chars = ace_yy_n_chars = 0; + + else + { + int num_to_read = + ace_yy_current_buffer->ace_yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef ACE_YY_USES_REJECT + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT") ); +#else + + /* just a shorter name for the current buffer */ + ACE_YY_BUFFER_STATE b = ace_yy_current_buffer; + + int ace_yy_c_buf_p_offset = + (int) (ace_yy_c_buf_p - b->ace_yy_ch_buf); + + if ( b->ace_yy_is_our_buffer ) + { + int new_size = b->ace_yy_buf_size * 2; + + if ( new_size <= 0 ) + b->ace_yy_buf_size += b->ace_yy_buf_size / 8; + else + b->ace_yy_buf_size *= 2; + + b->ace_yy_ch_buf = (ACE_TCHAR *) + /* Include room in for 2 EOB chars. */ + ace_yy_flex_realloc( (void *) b->ace_yy_ch_buf, + (b->ace_yy_buf_size + 2)*sizeof(ACE_TCHAR)); + } + else + /* Can't grow it, we don't own it. */ + b->ace_yy_ch_buf = 0; + + if ( ! b->ace_yy_ch_buf ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( + "fatal error - scanner input buffer overflow") ); + + ace_yy_c_buf_p = &b->ace_yy_ch_buf[ace_yy_c_buf_p_offset]; + + num_to_read = ace_yy_current_buffer->ace_yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read * sizeof (ACE_TCHAR) > ACE_YY_READ_BUF_SIZE ) + num_to_read = ACE_YY_READ_BUF_SIZE/sizeof (ACE_TCHAR); + + /* Read in more data. */ + ACE_YY_INPUT( (&ace_yy_current_buffer->ace_yy_ch_buf[number_to_move]), + ace_yy_n_chars, num_to_read ); + + ace_yy_current_buffer->ace_yy_n_chars = ace_yy_n_chars; + } + + if ( ace_yy_n_chars == 0 ) + { + if ( number_to_move == ACE_YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + ace_yyrestart( ace_yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + ace_yy_current_buffer->ace_yy_buffer_status = + ACE_YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + ace_yy_n_chars += number_to_move; + ace_yy_current_buffer->ace_yy_ch_buf[ace_yy_n_chars] = ACE_YY_END_OF_BUFFER_CHAR; + ace_yy_current_buffer->ace_yy_ch_buf[ace_yy_n_chars + 1] = ACE_YY_END_OF_BUFFER_CHAR; + + ace_yytext_ptr = &ace_yy_current_buffer->ace_yy_ch_buf[0]; + + return ret_val; + } + + +/* ace_yy_get_previous_state - get the state just before the EOB ACE_TCHAR was reached */ + +static ace_yy_state_type ace_yy_get_previous_state() + { + register ace_yy_state_type ace_yy_current_state; + register ACE_TCHAR *ace_yy_cp; + + ace_yy_current_state = ace_yy_start; + ace_yy_current_state += ACE_YY_AT_BOL(); + + for ( ace_yy_cp = ace_yytext_ptr + ACE_YY_MORE_ADJ; ace_yy_cp < ace_yy_c_buf_p; ++ace_yy_cp ) + { + register ACE_YY_CHAR ace_yy_c = (*ace_yy_cp ? ace_yy_ec[ACE_YY_SC_TO_UI(*ace_yy_cp)] : 1); + if ( ace_yy_accept[ace_yy_current_state] ) + { + ace_yy_last_accepting_state = ace_yy_current_state; + ace_yy_last_accepting_cpos = ace_yy_cp; + } + while ( ace_yy_chk[ace_yy_base[ace_yy_current_state] + ace_yy_c] != ace_yy_current_state ) + { + ace_yy_current_state = (int) ace_yy_def[ace_yy_current_state]; + if ( ace_yy_current_state >= 107 ) + ace_yy_c = ace_yy_meta[(unsigned int) ace_yy_c]; + } + ace_yy_current_state = ace_yy_nxt[ace_yy_base[ace_yy_current_state] + (unsigned int) ace_yy_c]; + } + + return ace_yy_current_state; + } + + +/* ace_yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = ace_yy_try_NUL_trans( current_state ); + */ + +#ifdef ACE_YY_USE_PROTOS +static ace_yy_state_type ace_yy_try_NUL_trans( ace_yy_state_type ace_yy_current_state ) +#else +static ace_yy_state_type ace_yy_try_NUL_trans( ace_yy_current_state ) +ace_yy_state_type ace_yy_current_state; +#endif + { + register int ace_yy_is_jam; + register ACE_TCHAR *ace_yy_cp = ace_yy_c_buf_p; + + register ACE_YY_CHAR ace_yy_c = 1; + if ( ace_yy_accept[ace_yy_current_state] ) + { + ace_yy_last_accepting_state = ace_yy_current_state; + ace_yy_last_accepting_cpos = ace_yy_cp; + } + while ( ace_yy_chk[ace_yy_base[ace_yy_current_state] + ace_yy_c] != ace_yy_current_state ) + { + ace_yy_current_state = (int) ace_yy_def[ace_yy_current_state]; + if ( ace_yy_current_state >= 107 ) + ace_yy_c = ace_yy_meta[(unsigned int) ace_yy_c]; + } + ace_yy_current_state = ace_yy_nxt[ace_yy_base[ace_yy_current_state] + (unsigned int) ace_yy_c]; + ace_yy_is_jam = (ace_yy_current_state == 106); + + return ace_yy_is_jam ? 0 : ace_yy_current_state; + } + + +#ifndef ACE_YY_NO_UNPUT +#ifdef ACE_YY_USE_PROTOS +static void ace_yyunput( int c, register ACE_TCHAR *ace_yy_bp ) +#else +static void ace_yyunput( c, ace_yy_bp ) +int c; +register ACE_TCHAR *ace_yy_bp; +#endif + { + register ACE_TCHAR *ace_yy_cp = ace_yy_c_buf_p; + + /* undo effects of setting up ace_yytext */ + *ace_yy_cp = ace_yy_hold_char; + + if ( ace_yy_cp < ace_yy_current_buffer->ace_yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = ace_yy_n_chars + 2; + register ACE_TCHAR *dest = &ace_yy_current_buffer->ace_yy_ch_buf[ + ace_yy_current_buffer->ace_yy_buf_size + 2]; + register ACE_TCHAR *source = + &ace_yy_current_buffer->ace_yy_ch_buf[number_to_move]; + + while ( source > ace_yy_current_buffer->ace_yy_ch_buf ) + *--dest = *--source; + + ace_yy_cp += (int) (dest - source); + ace_yy_bp += (int) (dest - source); + ace_yy_current_buffer->ace_yy_n_chars = + ace_yy_n_chars = ace_yy_current_buffer->ace_yy_buf_size; + + if ( ace_yy_cp < ace_yy_current_buffer->ace_yy_ch_buf + 2 ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "flex scanner push-back overflow") ); + } + + *--ace_yy_cp = (ACE_TCHAR) c; + + + ace_yytext_ptr = ace_yy_bp; + ace_yy_hold_char = *ace_yy_cp; + ace_yy_c_buf_p = ace_yy_cp; + } +#endif /* ifndef ACE_YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int ace_yyinput() +#else +static int input() +#endif + { + int c; + + *ace_yy_c_buf_p = ace_yy_hold_char; + + if ( *ace_yy_c_buf_p == ACE_YY_END_OF_BUFFER_CHAR ) + { + /* ace_yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( ace_yy_c_buf_p < &ace_yy_current_buffer->ace_yy_ch_buf[ace_yy_n_chars] ) + /* This was really a NUL. */ + *ace_yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = ace_yy_c_buf_p - ace_yytext_ptr; + ++ace_yy_c_buf_p; + + switch ( ace_yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because ace_yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + ace_yyrestart( ace_yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( ace_yywrap() ) + return EOF; + + if ( ! ace_yy_did_buffer_switch_on_eof ) + ACE_YY_NEW_FILE; +#ifdef __cplusplus + return ace_yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + ace_yy_c_buf_p = ace_yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) ace_yy_c_buf_p; /* cast for 8-bit char's */ + *ace_yy_c_buf_p = '\0'; /* preserve ace_yytext */ + ace_yy_hold_char = *++ace_yy_c_buf_p; + + ace_yy_current_buffer->ace_yy_at_bol = (c == '\n'); + + return c; + } + + +#ifdef ACE_YY_USE_PROTOS +void ace_yyrestart( FILE *input_file ) +#else +void ace_yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! ace_yy_current_buffer ) + ace_yy_current_buffer = ace_yy_create_buffer( ace_yyin, ACE_YY_BUF_SIZE ); + + ace_yy_init_buffer( ace_yy_current_buffer, input_file ); + ace_yy_load_buffer_state(); + } + + +#ifdef ACE_YY_USE_PROTOS +void ace_yy_switch_to_buffer( ACE_YY_BUFFER_STATE new_buffer ) +#else +void ace_yy_switch_to_buffer( new_buffer ) +ACE_YY_BUFFER_STATE new_buffer; +#endif + { + if ( ace_yy_current_buffer == new_buffer ) + return; + + if ( ace_yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *ace_yy_c_buf_p = ace_yy_hold_char; + ace_yy_current_buffer->ace_yy_buf_pos = ace_yy_c_buf_p; + ace_yy_current_buffer->ace_yy_n_chars = ace_yy_n_chars; + } + + ace_yy_current_buffer = new_buffer; + ace_yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (ace_yywrap()) processing, but the only time this flag + * is looked at is after ace_yywrap() is called, so it's safe + * to go ahead and always set it. + */ + ace_yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef ACE_YY_USE_PROTOS +void ace_yy_load_buffer_state( void ) +#else +void ace_yy_load_buffer_state() +#endif + { + ace_yy_n_chars = ace_yy_current_buffer->ace_yy_n_chars; + ace_yytext_ptr = ace_yy_c_buf_p = ace_yy_current_buffer->ace_yy_buf_pos; + ace_yyin = ace_yy_current_buffer->ace_yy_input_file; + ace_yy_hold_char = *ace_yy_c_buf_p; + } + + +#ifdef ACE_YY_USE_PROTOS +ACE_YY_BUFFER_STATE ace_yy_create_buffer( FILE *file, int size ) +#else +ACE_YY_BUFFER_STATE ace_yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + ACE_YY_BUFFER_STATE b; + + b = (ACE_YY_BUFFER_STATE) ace_yy_flex_alloc( sizeof( struct ace_yy_buffer_state ) ); + if ( ! b ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "out of dynamic memory in ace_yy_create_buffer()") ); + + b->ace_yy_buf_size = size; + + /* ace_yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->ace_yy_ch_buf = (ACE_TCHAR *) ace_yy_flex_alloc( (b->ace_yy_buf_size + 2 ) * sizeof (ACE_TCHAR)); + if ( ! b->ace_yy_ch_buf ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "out of dynamic memory in ace_yy_create_buffer()") ); + + b->ace_yy_is_our_buffer = 1; + + ace_yy_init_buffer( b, file ); + + return b; + } + + +#ifdef ACE_YY_USE_PROTOS +void ace_yy_delete_buffer( ACE_YY_BUFFER_STATE b ) +#else +void ace_yy_delete_buffer( b ) +ACE_YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == ace_yy_current_buffer ) + ace_yy_current_buffer = (ACE_YY_BUFFER_STATE) 0; + + if ( b->ace_yy_is_our_buffer ) + ace_yy_flex_free( (void *) b->ace_yy_ch_buf ); + + ace_yy_flex_free( (void *) b ); + } + + +#ifndef ACE_YY_ALWAYS_INTERACTIVE +#ifndef ACE_YY_NEVER_INTERACTIVE +extern int nop_isatty ACE_YY_PROTO(( int )); +#endif +#endif + +#ifdef ACE_YY_USE_PROTOS +void ace_yy_init_buffer( ACE_YY_BUFFER_STATE b, FILE *file ) +#else +void ace_yy_init_buffer( b, file ) +ACE_YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + ace_yy_flush_buffer( b ); + + b->ace_yy_input_file = file; + b->ace_yy_fill_buffer = 1; + +#if ACE_YY_ALWAYS_INTERACTIVE + b->ace_yy_is_interactive = 1; +#else +#if ACE_YY_NEVER_INTERACTIVE + b->ace_yy_is_interactive = 0; +#else + b->ace_yy_is_interactive = file ? (ACE_OS::isatty( fileno (file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef ACE_YY_USE_PROTOS +void ace_yy_flush_buffer( ACE_YY_BUFFER_STATE b ) +#else +void ace_yy_flush_buffer( b ) +ACE_YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->ace_yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->ace_yy_ch_buf[0] = ACE_YY_END_OF_BUFFER_CHAR; + b->ace_yy_ch_buf[1] = ACE_YY_END_OF_BUFFER_CHAR; + + b->ace_yy_buf_pos = &b->ace_yy_ch_buf[0]; + + b->ace_yy_at_bol = 1; + b->ace_yy_buffer_status = ACE_YY_BUFFER_NEW; + + if ( b == ace_yy_current_buffer ) + ace_yy_load_buffer_state(); + } + + +#ifndef ACE_YY_NO_SCAN_BUFFER +#ifdef ACE_YY_USE_PROTOS +ACE_YY_BUFFER_STATE ace_yy_scan_buffer( ACE_TCHAR *base, ace_yy_size_t size ) +#else +ACE_YY_BUFFER_STATE ace_yy_scan_buffer( base, size ) +ACE_TCHAR *base; +ace_yy_size_t size; +#endif + { + ACE_YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != ACE_YY_END_OF_BUFFER_CHAR || + base[size-1] != ACE_YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (ACE_YY_BUFFER_STATE) ace_yy_flex_alloc( sizeof( struct ace_yy_buffer_state ) ); + if ( ! b ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "out of dynamic memory in ace_yy_scan_buffer()" )); + + b->ace_yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->ace_yy_buf_pos = b->ace_yy_ch_buf = base; + b->ace_yy_is_our_buffer = 0; + b->ace_yy_input_file = 0; + b->ace_yy_n_chars = b->ace_yy_buf_size; + b->ace_yy_is_interactive = 0; + b->ace_yy_at_bol = 1; + b->ace_yy_fill_buffer = 0; + b->ace_yy_buffer_status = ACE_YY_BUFFER_NEW; + + ace_yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef ACE_YY_NO_SCAN_STRING +#ifdef ACE_YY_USE_PROTOS +ACE_YY_BUFFER_STATE ace_yy_scan_string( ace_yyconst ACE_TCHAR *ace_yy_str ) +#else +ACE_YY_BUFFER_STATE ace_yy_scan_string( ace_yy_str ) +ace_yyconst ACE_TCHAR *ace_yy_str; +#endif + { + int len; + for ( len = 0; ace_yy_str[len]; ++len ) + ; + + return ace_yy_scan_bytes( ace_yy_str, len ); + } +#endif + + +#ifndef ACE_YY_NO_SCAN_BYTES +#ifdef ACE_YY_USE_PROTOS +ACE_YY_BUFFER_STATE ace_yy_scan_bytes( ace_yyconst ACE_TCHAR *bytes, int len ) +#else +ACE_YY_BUFFER_STATE ace_yy_scan_bytes( bytes, len ) +ace_yyconst ACE_TCHAR *bytes; +int len; +#endif + { + ACE_YY_BUFFER_STATE b; + ACE_TCHAR *buf; + ace_yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (ACE_TCHAR *) ace_yy_flex_alloc( n * sizeof (ACE_TCHAR)); + if ( ! buf ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "out of dynamic memory in ace_yy_scan_bytes()" )); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = ACE_YY_END_OF_BUFFER_CHAR; + + b = ace_yy_scan_buffer( buf, n ); + if ( ! b ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "bad buffer in ace_yy_scan_bytes()") ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->ace_yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef ACE_YY_NO_PUSH_STATE +#ifdef ACE_YY_USE_PROTOS +static void ace_yy_push_state( int new_state ) +#else +static void ace_yy_push_state( new_state ) +int new_state; +#endif + { + if ( ace_yy_start_stack_ptr >= ace_yy_start_stack_depth ) + { + ace_yy_size_t new_size; + + ace_yy_start_stack_depth += ACE_YY_START_STACK_INCR; + new_size = ace_yy_start_stack_depth * sizeof( int ); + + if ( ! ace_yy_start_stack ) + ace_yy_start_stack = (int *) ace_yy_flex_alloc( new_size ); + + else + ace_yy_start_stack = (int *) ace_yy_flex_realloc( + (void *) ace_yy_start_stack, new_size ); + + if ( ! ace_yy_start_stack ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( + "out of memory expanding start-condition stack" )); + } + + ace_yy_start_stack[ace_yy_start_stack_ptr++] = ACE_YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef ACE_YY_NO_POP_STATE +static void ace_yy_pop_state() + { + if ( --ace_yy_start_stack_ptr < 0 ) + ACE_YY_FATAL_ERROR(ACE_LIB_TEXT( "start-condition stack underflow" )); + + BEGIN(ace_yy_start_stack[ace_yy_start_stack_ptr]); + } +#endif + + +#ifndef ACE_YY_NO_TOP_STATE +static int ace_yy_top_state() + { + return ace_yy_start_stack[ace_yy_start_stack_ptr - 1]; + } +#endif + +#ifndef ACE_YY_EXIT_FAILURE +#define ACE_YY_EXIT_FAILURE 2 +#endif + +#ifdef ACE_YY_USE_PROTOS +static void ace_yy_fatal_error( ace_yyconst ACE_TCHAR msg[] ) +#else +static void ace_yy_fatal_error( msg ) +ACE_TCHAR msg[]; +#endif + { + (void) ACE_OS::fprintf( stderr, ACE_LIB_TEXT("%s\n"), msg ); + exit( ACE_YY_EXIT_FAILURE ); + } + + + +/* Redefine ace_yyless() so it works in section 3 code. */ + +#undef ace_yyless +#define ace_yyless(n) \ + do \ + { \ + /* Undo effects of setting up ace_yytext. */ \ + ace_yytext[ace_yyleng] = ace_yy_hold_char; \ + ace_yy_c_buf_p = ace_yytext + n; \ + ace_yy_hold_char = *ace_yy_c_buf_p; \ + *ace_yy_c_buf_p = '\0'; \ + ace_yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef ace_yytext_ptr +#ifdef ACE_YY_USE_PROTOS +static void ace_yy_flex_strncpy( ACE_TCHAR *s1, ace_yyconst ACE_TCHAR *s2, int n ) +#else +static void ace_yy_flex_strncpy( s1, s2, n ) +ACE_TCHAR *s1; +ace_yyconst ACE_TCHAR *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef ACE_YY_NEED_STRLEN +#ifdef ACE_YY_USE_PROTOS +static int ace_yy_flex_strlen( ace_yyconst ACE_TCHAR *s ) +#else +static int ace_yy_flex_strlen( s ) +ace_yyconst ACE_TCHAR *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef ACE_YY_USE_PROTOS +static void *ace_yy_flex_alloc( ace_yy_size_t size ) +#else +static void *ace_yy_flex_alloc( size ) +ace_yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef ACE_YY_USE_PROTOS +static void *ace_yy_flex_realloc( void *ptr, ace_yy_size_t size ) +#else +static void *ace_yy_flex_realloc( ptr, size ) +void *ptr; +ace_yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef ACE_YY_USE_PROTOS +static void ace_yy_flex_free( void *ptr ) +#else +static void ace_yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ACE_MALLOC_T (ptr) ); + } + +#if ACE_YY_MAIN +int main() + { + ace_yylex(); + return 0; + } +#endif + +int +ace_yywrap (void) +{ + ::fflush (ace_yyin); + ace_yytext[0] = '#'; + ace_yyleng = 0; + + return 1; +} + +void +ace_yy_push_buffer (FILE *file, ace_yy_buffer_state *&buffer) +{ + // External synchronization is required. + + if (buffer == 0) + buffer = ace_yy_create_buffer (file, ACE_YY_BUF_SIZE); + + ace_yy_switch_to_buffer (buffer); +} + +void +ace_yy_push_buffer (const ACE_TCHAR *directive, ace_yy_buffer_state *&buffer) +{ + // External synchronization is required. + + // ace_yyparse() may invoke ace_yylex() multiple times when parsing + // a single directive. Prevent a new buffer from created during + // each call to ace_yylex(). + if (ACE_YY_CURRENT_BUFFER != 0 + && directive == ACE_YY_CURRENT_BUFFER->ace_yy_ch_buf) + return; + + if (buffer == 0) + { + // ace_yy_scan_string() already switches the buffer so there is + // no need to explicitly make the switch. + buffer = ace_yy_scan_string (directive); + } + else + ace_yy_switch_to_buffer (buffer); +} + +void +ace_yy_pop_buffer (ace_yy_buffer_state *buffer) +{ + // External synchronization is required. + + ace_yy_switch_to_buffer (buffer); +} diff --git a/ace/Svcconf/Svc_Conf_y.cpp b/ace/Svcconf/Svc_Conf_y.cpp new file mode 100644 index 00000000000..5c76230b920 --- /dev/null +++ b/ace/Svcconf/Svc_Conf_y.cpp @@ -0,0 +1,1406 @@ + +/* A Bison parser, made from Svc_Conf.y + by GNU Bison version 1.28 */ + +#define ACE_YYBISON 1 /* Identify Bison output. */ + +#define ACE_DYNAMIC 257 +#define ACE_STATIC 258 +#define ACE_SUSPEND 259 +#define ACE_RESUME 260 +#define ACE_REMOVE 261 +#define ACE_USTREAM 262 +#define ACE_MODULE_T 263 +#define ACE_STREAM_T 264 +#define ACE_SVC_OBJ_T 265 +#define ACE_ACTIVE 266 +#define ACE_INACTIVE 267 +#define ACE_PATHNAME 268 +#define ACE_IDENT 269 +#define ACE_STRING 270 + + +// $Id$ + +#include "ace/ARGV.h" +#include "ace/Svc_Conf.h" +#include "ace/Module.h" +#include "ace/Stream.h" + +ACE_RCSID (ace, + Svc_Conf_y, + "$Id$") + +// Prototypes. +static ACE_Module_Type *ace_get_module (ACE_Static_Node *str_rec, + ACE_Static_Node *svc_type); +static ACE_Module_Type *ace_get_module (ACE_Static_Node *str_rec, + const ACE_TCHAR *svc_name); + +#define ACE_YYDEBUG_LEXER_TEXT (ace_yytext[ace_yyleng] = '\0', ace_yytext) + +// Force the pretty debugging code to compile. +// #define ACE_YYDEBUG 1 + +// Keeps track of the number of errors encountered so far. +int ace_yyerrno = 0; + +#include + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define ACE_YYFINAL 66 +#define ACE_YYFLAG -32768 +#define ACE_YYNTBASE 23 + +#define ACE_YYTRANSLATE(x) ((unsigned)(x) <= 270 ? ace_yytranslate[x] : 43) + +static const ACE_TCHAR ace_yytranslate[] = { 0, + 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, 2, 2, 2, 2, 2, 2, 2, 20, + 21, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 19, 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, + 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, 17, 2, 18, 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, 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, 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, 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, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +}; + +#if ACE_YYDEBUG != 0 +static const short ace_yyprhs[] = { 0, + 0, 3, 6, 7, 9, 11, 13, 15, 17, 19, + 23, 27, 30, 33, 36, 40, 41, 46, 48, 50, + 51, 56, 57, 60, 61, 63, 65, 67, 69, 71, + 76, 78, 80, 81, 85, 91, 96, 99, 102, 105, + 107, 108, 110, 112 +}; + +static const short ace_yyrhs[] = { 23, + 24, 0, 23, 1, 0, 0, 25, 0, 26, 0, + 27, 0, 28, 0, 29, 0, 30, 0, 3, 37, + 41, 0, 4, 15, 41, 0, 5, 15, 0, 6, + 15, 0, 7, 15, 0, 8, 32, 33, 0, 0, + 8, 15, 31, 33, 0, 25, 0, 26, 0, 0, + 17, 34, 35, 18, 0, 0, 35, 36, 0, 0, + 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, + 15, 40, 39, 38, 0, 12, 0, 13, 0, 0, + 42, 19, 15, 0, 42, 19, 15, 20, 21, 0, + 19, 15, 20, 21, 0, 9, 22, 0, 11, 22, + 0, 10, 22, 0, 16, 0, 0, 14, 0, 15, + 0, 16, 0 +}; + +#endif + +#if ACE_YYDEBUG != 0 +static const short ace_yyrline[] = { 0, + 49, 57, 61, 65, 66, 67, 68, 69, 70, 74, + 84, 91, 98, 105, 112, 116, 117, 123, 126, 132, + 137, 141, 145, 153, 157, 183, 194, 201, 208, 231, + 265, 269, 273, 280, 284, 288, 295, 299, 303, 310, + 311, 315, 316, 317 +}; +#endif + + +#if ACE_YYDEBUG != 0 || defined (ACE_YYERROR_VERBOSE) + +static const ACE_TCHAR * const ace_yytname[] = { ACE_LIB_TEXT("$"), + ACE_LIB_TEXT("error"), + ACE_LIB_TEXT("$undefined."), + ACE_LIB_TEXT("ACE_DYNAMIC"), + ACE_LIB_TEXT("ACE_STATIC"), + ACE_LIB_TEXT("ACE_SUSPEND"), + ACE_LIB_TEXT("ACE_RESUME"), + ACE_LIB_TEXT("ACE_REMOVE"), + ACE_LIB_TEXT("ACE_USTREAM"), + ACE_LIB_TEXT("ACE_MODULE_T"), + ACE_LIB_TEXT("ACE_STREAM_T"), + ACE_LIB_TEXT("ACE_SVC_OBJ_T"), + ACE_LIB_TEXT("ACE_ACTIVE"), + ACE_LIB_TEXT("ACE_INACTIVE"), + ACE_LIB_TEXT("ACE_PATHNAME"), + ACE_LIB_TEXT("ACE_IDENT"), + ACE_LIB_TEXT("ACE_STRING"), + ACE_LIB_TEXT("'{'"), + ACE_LIB_TEXT("'}'"), + ACE_LIB_TEXT("':'"), + ACE_LIB_TEXT("'('"), + ACE_LIB_TEXT("')'"), + ACE_LIB_TEXT("'*'"), + ACE_LIB_TEXT("svc_config_entries"), + ACE_LIB_TEXT("svc_config_entry"), + ACE_LIB_TEXT("dynamic"), + ACE_LIB_TEXT("static"), + ACE_LIB_TEXT("suspend"), + ACE_LIB_TEXT("resume"), + ACE_LIB_TEXT("remove"), + ACE_LIB_TEXT("stream"), + ACE_LIB_TEXT("@1"), + ACE_LIB_TEXT("stream_ops"), + ACE_LIB_TEXT("stream_modules"), + + ACE_LIB_TEXT("@2"), + ACE_LIB_TEXT("module_list"), + ACE_LIB_TEXT("module"), + ACE_LIB_TEXT("svc_location"), + ACE_LIB_TEXT("status"), + ACE_LIB_TEXT("svc_initializer"), + ACE_LIB_TEXT("type"), + ACE_LIB_TEXT("parameters_opt"), + ACE_LIB_TEXT("pathname"), + NULL +}; +#endif + +static const short ace_yyr1[] = { 0, + 23, 23, 23, 24, 24, 24, 24, 24, 24, 25, + 26, 27, 28, 29, 30, 31, 30, 32, 32, 34, + 33, 33, 35, 35, 36, 36, 36, 36, 36, 37, + 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, + 41, 42, 42, 42 +}; + +static const short ace_yyr2[] = { 0, + 2, 2, 0, 1, 1, 1, 1, 1, 1, 3, + 3, 2, 2, 2, 3, 0, 4, 1, 1, 0, + 4, 0, 2, 0, 1, 1, 1, 1, 1, 4, + 1, 1, 0, 3, 5, 4, 2, 2, 2, 1, + 0, 1, 1, 1 +}; + +static const short ace_yydefact[] = { 3, + 0, 2, 0, 0, 0, 0, 0, 0, 1, 4, + 5, 6, 7, 8, 9, 0, 41, 41, 12, 13, + 14, 16, 18, 19, 22, 0, 0, 0, 0, 40, + 10, 11, 22, 20, 15, 37, 39, 38, 42, 43, + 44, 0, 33, 0, 17, 24, 0, 31, 32, 30, + 0, 0, 0, 34, 21, 25, 26, 27, 28, 29, + 23, 36, 0, 35, 0, 0 +}; + +static const short ace_yydefgoto[] = { 1, + 9, 10, 11, 12, 13, 14, 15, 33, 25, 35, + 46, 52, 61, 17, 50, 43, 29, 31, 44 +}; + +static const short ace_yypact[] = {-32768, + 20,-32768, 1, 3, 7, 14, 18, 4,-32768,-32768, +-32768,-32768,-32768,-32768,-32768, 21, 19, 19,-32768,-32768, +-32768,-32768,-32768,-32768, -2, 12, 15, 16, -5,-32768, +-32768,-32768, -2,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768, 24, 0, 17,-32768,-32768, 22,-32768,-32768,-32768, + 25, -1, 26, 23,-32768,-32768,-32768,-32768,-32768,-32768, +-32768,-32768, 27,-32768, 41,-32768 +}; + +static const short ace_yypgoto[] = {-32768, +-32768, -8, -7, -6, -3, 2,-32768,-32768,-32768, 28, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 32,-32768 +}; + + +#define ACE_YYLAST 61 + + +static const short ace_yytable[] = { 23, + 24, 3, 4, 5, 6, 7, 3, 4, 39, 40, + 41, 48, 49, 42, 34, 16, 55, 18, 22, 65, + 2, 19, 3, 4, 5, 6, 7, 8, 20, 26, + 27, 28, 21, 36, 30, 51, 37, 38, 47, 54, + 66, 53, 63, 56, 57, 58, 62, 64, 59, 32, + 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, + 45 +}; + +static const short ace_yycheck[] = { 8, + 8, 3, 4, 5, 6, 7, 3, 4, 14, 15, + 16, 12, 13, 19, 17, 15, 18, 15, 15, 0, + 1, 15, 3, 4, 5, 6, 7, 8, 15, 9, + 10, 11, 15, 22, 16, 19, 22, 22, 15, 15, + 0, 20, 20, 52, 52, 52, 21, 21, 52, 18, + -1, -1, -1, 52, -1, -1, -1, -1, -1, -1, + 33 +}; +#define ACE_YYPURE 1 + +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ + +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef ACE_YYSTACK_USE_ALLOCA +#ifdef alloca +#define ACE_YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define ACE_YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define ACE_YYSTACK_USE_ALLOCA +#include +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include +#endif +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include */ + #pragma alloca +#define ACE_YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define ACE_YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ +#endif +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* ACE_YYSTACK_USE_ALLOCA not defined */ + +#ifdef ACE_YYSTACK_USE_ALLOCA +#define ACE_YYSTACK_ALLOC alloca +#else +#define ACE_YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define ace_yyerrok (ace_yyerrstatus = 0) +#define ace_yyclearin (ace_yychar = ACE_YYEMPTY) +#define ACE_YYEMPTY -2 +#define ACE_YYEOF 0 +#define ACE_YYACCEPT goto ace_yyacceptlab +#define ACE_YYABORT goto ace_yyabortlab +#define ACE_YYERROR goto ace_yyerrlab1 +/* Like ACE_YYERROR except do call ace_yyerror. + This remains here temporarily to ease the + transition to the new meaning of ACE_YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define ACE_YYFAIL goto ace_yyerrlab +#define ACE_YYRECOVERING() (!!ace_yyerrstatus) +#define ACE_YYBACKUP(token, value) \ +do \ + if (ace_yychar == ACE_YYEMPTY && ace_yylen == 1) \ + { ace_yychar = (token), ace_yylval = (value); \ + ace_yychar1 = ACE_YYTRANSLATE (ace_yychar); \ + ACE_YYPOPSTACK; \ + goto ace_yybackup; \ + } \ + else \ + { ace_yyerror (ACE_LIB_TEXT("syntax error: cannot back up")); ACE_YYERROR; } \ +while (0) + +#define ACE_YYTERROR 1 +#define ACE_YYERRCODE 256 + +#ifndef ACE_YYPURE +#define ACE_YYLEX ace_yylex() +#endif + +#ifdef ACE_YYPURE +#ifdef ACE_YYLSP_NEEDED +#ifdef ACE_YYLEX_PARAM +#define ACE_YYLEX ace_yylex(&ace_yylval, &ace_yylloc, ACE_YYLEX_PARAM) +#else +#define ACE_YYLEX ace_yylex(&ace_yylval, &ace_yylloc) +#endif +#else /* not ACE_YYLSP_NEEDED */ +#ifdef ACE_YYLEX_PARAM +#define ACE_YYLEX ace_yylex(&ace_yylval, ACE_YYLEX_PARAM) +#else +#define ACE_YYLEX ace_yylex(&ace_yylval) +#endif +#endif /* not ACE_YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef ACE_YYPURE + +int ace_yychar; /* the lookahead symbol */ +ACE_YYSTYPE ace_yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef ACE_YYLSP_NEEDED +ACE_YYLTYPE ace_yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int ace_yynerrs; /* number of parse errors so far */ +#endif /* not ACE_YYPURE */ + +#if ACE_YYDEBUG != 0 +int ace_yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* ACE_YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef ACE_YYINITDEPTH +#define ACE_YYINITDEPTH 200 +#endif + +/* ACE_YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if ACE_YYMAXDEPTH == 0 +#undef ACE_YYMAXDEPTH +#endif + +#ifndef ACE_YYMAXDEPTH +#define ACE_YYMAXDEPTH 10000 +#endif + +/* Define __ace_yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __ace_yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__ace_yy_memcpy (to, from, count) + ACE_TCHAR *to; + ACE_TCHAR *from; + unsigned int count; +{ + register ACE_TCHAR *f = from; + register ACE_TCHAR *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__ace_yy_memcpy (ACE_TCHAR *to, ACE_TCHAR *from, unsigned int count) +{ + register ACE_TCHAR *t = to; + register ACE_TCHAR *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + + + +/* The user can define ACE_YYPARSE_PARAM as the name of an argument to be passed + into ace_yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef ACE_YYPARSE_PARAM +#ifdef __cplusplus +#define ACE_YYPARSE_PARAM_ARG void *ACE_YYPARSE_PARAM +#define ACE_YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define ACE_YYPARSE_PARAM_ARG ACE_YYPARSE_PARAM +#define ACE_YYPARSE_PARAM_DECL void *ACE_YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not ACE_YYPARSE_PARAM */ +#define ACE_YYPARSE_PARAM_ARG +#define ACE_YYPARSE_PARAM_DECL +#endif /* not ACE_YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +#ifdef ACE_YYPARSE_PARAM +int ace_yyparse (void *); +#else +int ace_yyparse (void); +#endif +#endif + +int +ace_yyparse(ACE_YYPARSE_PARAM_ARG) + ACE_YYPARSE_PARAM_DECL +{ + register int ace_yystate; + register int ace_yyn; + register short *ace_yyssp; + register ACE_YYSTYPE *ace_yyvsp; + int ace_yyerrstatus; /* number of tokens to shift before error messages enabled */ + int ace_yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short ace_yyssa[ACE_YYINITDEPTH]; /* the state stack */ + ACE_YYSTYPE ace_yyvsa[ACE_YYINITDEPTH]; /* the semantic value stack */ + + short *ace_yyss = ace_yyssa; /* refer to the stacks thru separate pointers */ + ACE_YYSTYPE *ace_yyvs = ace_yyvsa; /* to allow ace_yyoverflow to reallocate them elsewhere */ + +#ifdef ACE_YYLSP_NEEDED + ACE_YYLTYPE ace_yylsa[ACE_YYINITDEPTH]; /* the location stack */ + ACE_YYLTYPE *ace_yyls = ace_yylsa; + ACE_YYLTYPE *ace_yylsp; + +#define ACE_YYPOPSTACK (ace_yyvsp--, ace_yyssp--, ace_yylsp--) +#else +#define ACE_YYPOPSTACK (ace_yyvsp--, ace_yyssp--) +#endif + + int ace_yystacksize = ACE_YYINITDEPTH; + int ace_yyfree_stacks = 0; + +#ifdef ACE_YYPURE + int ace_yychar; + ACE_YYSTYPE ace_yylval; + int ace_yynerrs; +#ifdef ACE_YYLSP_NEEDED + ACE_YYLTYPE ace_yylloc; +#endif +#endif + + ACE_YYSTYPE ace_yyval; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int ace_yylen; + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Starting parse\n")); +#endif + + ace_yystate = 0; + ace_yyerrstatus = 0; + ace_yynerrs = 0; + ace_yychar = ACE_YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + ace_yyssp = ace_yyss - 1; + ace_yyvsp = ace_yyvs; +#ifdef ACE_YYLSP_NEEDED + ace_yylsp = ace_yyls; +#endif + +/* Push a new state, which is found in ace_yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +ace_yynewstate: + + *++ace_yyssp = ace_yystate; + + if (ace_yyssp >= ace_yyss + ace_yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + ACE_YYSTYPE *ace_yyvs1 = ace_yyvs; + short *ace_yyss1 = ace_yyss; +#ifdef ACE_YYLSP_NEEDED + ACE_YYLTYPE *ace_yyls1 = ace_yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = ace_yyssp - ace_yyss + 1; + +#ifdef ace_yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef ACE_YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if ace_yyoverflow is a macro. */ + ace_yyoverflow(ACE_LIB_TEXT("parser stack overflow"), + &ace_yyss1, size * sizeof (*ace_yyssp), + &ace_yyvs1, size * sizeof (*ace_yyvsp), + &ace_yyls1, size * sizeof (*ace_yylsp), + &ace_yystacksize); +#else + ace_yyoverflow(ACE_LIB_TEXT("parser stack overflow"), + &ace_yyss1, size * sizeof (*ace_yyssp), + &ace_yyvs1, size * sizeof (*ace_yyvsp), + &ace_yystacksize); +#endif + + ace_yyss = ace_yyss1; ace_yyvs = ace_yyvs1; +#ifdef ACE_YYLSP_NEEDED + ace_yyls = ace_yyls1; +#endif +#else /* no ace_yyoverflow */ + /* Extend the stack our own way. */ + if (ace_yystacksize >= ACE_YYMAXDEPTH) + { + ace_yyerror(ACE_LIB_TEXT("parser stack overflow")); + if (ace_yyfree_stacks) + { + free (ace_yyss); + free (ace_yyvs); +#ifdef ACE_YYLSP_NEEDED + free (ace_yyls); +#endif + } + return 2; + } + ace_yystacksize *= 2; + if (ace_yystacksize > ACE_YYMAXDEPTH) + ace_yystacksize = ACE_YYMAXDEPTH; +#ifndef ACE_YYSTACK_USE_ALLOCA + ace_yyfree_stacks = 1; +#endif + ace_yyss = (short *) ACE_YYSTACK_ALLOC (ace_yystacksize * sizeof (*ace_yyssp)); + __ace_yy_memcpy ((ACE_TCHAR *)ace_yyss, (ACE_TCHAR *)ace_yyss1, + size * (unsigned int) sizeof (*ace_yyssp)); + ace_yyvs = (ACE_YYSTYPE *) ACE_YYSTACK_ALLOC (ace_yystacksize * sizeof (*ace_yyvsp)); + __ace_yy_memcpy ((ACE_TCHAR *)ace_yyvs, (ACE_TCHAR *)ace_yyvs1, + size * (unsigned int) sizeof (*ace_yyvsp)); +#ifdef ACE_YYLSP_NEEDED + ace_yyls = (ACE_YYLTYPE *) ACE_YYSTACK_ALLOC (ace_yystacksize * sizeof (*ace_yylsp)); + __ace_yy_memcpy ((ACE_TCHAR *)ace_yyls, (ACE_TCHAR *)ace_yyls1, + size * (unsigned int) sizeof (*ace_yylsp)); +#endif +#endif /* no ace_yyoverflow */ + + ace_yyssp = ace_yyss + size - 1; + ace_yyvsp = ace_yyvs + size - 1; +#ifdef ACE_YYLSP_NEEDED + ace_yylsp = ace_yyls + size - 1; +#endif + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Stack size increased to %d\n"), ace_yystacksize); +#endif + + if (ace_yyssp >= ace_yyss + ace_yystacksize - 1) + ACE_YYABORT; + } + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Entering state %d\n"), ace_yystate); +#endif + + goto ace_yybackup; + ace_yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* ace_yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + ace_yyn = ace_yypact[ace_yystate]; + if (ace_yyn == ACE_YYFLAG) + goto ace_yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* ace_yychar is either ACE_YYEMPTY or ACE_YYEOF + or a valid token in external form. */ + + if (ace_yychar == ACE_YYEMPTY) + { +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Reading a token: ")); +#endif + ace_yychar = ACE_YYLEX; + } + + /* Convert token to internal form (in ace_yychar1) for indexing tables with */ + + if (ace_yychar <= 0) /* This means end of input. */ + { + ace_yychar1 = 0; + ace_yychar = ACE_YYEOF; /* Don't call ACE_YYLEX any more */ + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Now at end of input.\n")); +#endif + } + else + { + ace_yychar1 = ACE_YYTRANSLATE(ace_yychar); + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + { + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("Next token is %d (%s"), ace_yychar, ace_yytname[ace_yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef ACE_YYPRINT + ACE_YYPRINT (stderr, ace_yychar, ace_yylval); +#endif + ACE_OS::fprintf (stderr, ACE_LIB_TEXT(")\n")); + } +#endif + } + + ace_yyn += ace_yychar1; + if (ace_yyn < 0 || ace_yyn > ACE_YYLAST || ace_yycheck[ace_yyn] != ace_yychar1) + goto ace_yydefault; + + ace_yyn = ace_yytable[ace_yyn]; + + /* ace_yyn is what to do for this token type in this state. + Negative => reduce, -ace_yyn is rule number. + Positive => shift, ace_yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (ace_yyn < 0) + { + if (ace_yyn == ACE_YYFLAG) + goto ace_yyerrlab; + ace_yyn = -ace_yyn; + goto ace_yyreduce; + } + else if (ace_yyn == 0) + goto ace_yyerrlab; + + if (ace_yyn == ACE_YYFINAL) + ACE_YYACCEPT; + + /* Shift the lookahead token. */ + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Shifting token %d (%s), "), ace_yychar, ace_yytname[ace_yychar1]); +#endif + + /* Discard the token being shifted unless it is eof. */ + if (ace_yychar != ACE_YYEOF) + ace_yychar = ACE_YYEMPTY; + + *++ace_yyvsp = ace_yylval; +#ifdef ACE_YYLSP_NEEDED + *++ace_yylsp = ace_yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (ace_yyerrstatus) ace_yyerrstatus--; + + ace_yystate = ace_yyn; + goto ace_yynewstate; + +/* Do the default action for the current state. */ +ace_yydefault: + + ace_yyn = ace_yydefact[ace_yystate]; + if (ace_yyn == 0) + goto ace_yyerrlab; + +/* Do a reduction. ace_yyn is the number of a rule to reduce with. */ +ace_yyreduce: + ace_yylen = ace_yyr2[ace_yyn]; + if (ace_yylen > 0) + ace_yyval = ace_yyvsp[1-ace_yylen]; /* implement default value of the action */ + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + { + int i; + + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("Reducing via rule %d (line %d), "), + ace_yyn, ace_yyrline[ace_yyn]); + + /* Print the symbols being reduced, and their result. */ + for (i = ace_yyprhs[ace_yyn]; ace_yyrhs[i] > 0; i++) + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("%s "), ace_yytname[ace_yyrhs[i]]); + ACE_OS::fprintf (stderr, ACE_LIB_TEXT(" -> %s\n"), ace_yytname[ace_yyr1[ace_yyn]]); + } +#endif + + + switch (ace_yyn) { + +case 1: +{ + if (ace_yyvsp[0].parse_node_ != 0) + { + ace_yyvsp[0].parse_node_->apply (); delete ace_yyvsp[0].parse_node_; + } + ACE_SVC_CONF_PARAM->obstack.release (); + ; + break;} +case 2: +{ + ACE_SVC_CONF_PARAM->obstack.release (); + ; + break;} +case 10: +{ + if (ace_yyvsp[-1].svc_record_ != 0) + ace_yyval.parse_node_ = new ACE_Dynamic_Node (ace_yyvsp[-1].svc_record_, ace_yyvsp[0].ident_); + else + ace_yyval.parse_node_ = 0; + ; + break;} +case 11: +{ + ace_yyval.parse_node_ = new ACE_Static_Node (ace_yyvsp[-1].ident_, ace_yyvsp[0].ident_); + ; + break;} +case 12: +{ + ace_yyval.parse_node_ = new ACE_Suspend_Node (ace_yyvsp[0].ident_); + ; + break;} +case 13: +{ + ace_yyval.parse_node_ = new ACE_Resume_Node (ace_yyvsp[0].ident_); + ; + break;} +case 14: +{ + ace_yyval.parse_node_ = new ACE_Remove_Node (ace_yyvsp[0].ident_); + ; + break;} +case 15: +{ + ace_yyval.parse_node_ = new ACE_Stream_Node (ace_yyvsp[-1].static_node_, ace_yyvsp[0].parse_node_); + ; + break;} +case 16: +{ ace_yyval.static_node_ = new ACE_Static_Node (ace_yyvsp[0].ident_); ; + break;} +case 17: +{ + ace_yyval.parse_node_ = new ACE_Dummy_Node (ace_yyvsp[-1].static_node_, ace_yyvsp[0].parse_node_); + ; + break;} +case 18: +{ + ; + break;} +case 19: +{ + ; + break;} +case 20: +{ + // Initialize left context... + ace_yyval.static_node_ = ace_yyvsp[-1].static_node_; + ; + break;} +case 21: +{ + ace_yyval.parse_node_ = ace_yyvsp[-1].parse_node_; + ; + break;} +case 22: +{ ace_yyval.parse_node_ = 0; ; + break;} +case 23: +{ + if (ace_yyvsp[0].parse_node_ != 0) + { + ace_yyvsp[0].parse_node_->link (ace_yyvsp[-1].parse_node_); + ace_yyval.parse_node_ = ace_yyvsp[0].parse_node_; + } + ; + break;} +case 24: +{ ace_yyval.parse_node_ = 0; ; + break;} +case 25: +{ + ACE_Static_Node *svc_type = ace_yyvsp[0].static_node_; + + if (svc_type != 0) + { + ACE_Static_Node *module = ace_yyvsp[-2].static_node_; + + ACE_ARGV args (svc_type->parameters ()); + ACE_Module_Type *mt = ace_get_module (module, + svc_type); + ACE_Stream_Type *st = + ACE_dynamic_cast (ACE_Stream_Type *, + ACE_const_cast (ACE_Service_Type_Impl *, + module->record ()->type ())); + + if (mt->init (args.argc (), args.argv ()) == -1 + || st->push (mt) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("dynamic initialization failed for Module %s\n"), + svc_type->name ())); + ACE_SVC_CONF_PARAM->yyerrno++; + } + } + ; + break;} +case 26: +{ + ACE_Module_Type *mt = ace_get_module (ace_yyvsp[-2].static_node_, ace_yyvsp[0].static_node_->name ()); + + if (((ACE_Stream_Type *) (ace_yyvsp[-2].static_node_)->record ()->type ())->push (mt) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("Problem with static\n"))); + ACE_SVC_CONF_PARAM->yyerrno++; + } + ; + break;} +case 27: +{ + ACE_Module_Type *mt = ace_get_module (ace_yyvsp[-2].static_node_, + ace_yyvsp[0].static_node_->name ()); + if (mt != 0) + mt->suspend (); + ; + break;} +case 28: +{ + ACE_Module_Type *mt = ace_get_module (ace_yyvsp[-2].static_node_, + ace_yyvsp[0].static_node_->name ()); + if (mt != 0) + mt->resume (); + ; + break;} +case 29: +{ + ACE_Static_Node *stream = ace_yyvsp[-2].static_node_; + ACE_Static_Node *module = ace_yyvsp[0].static_node_; + ACE_Module_Type *mt = ace_get_module (stream, + module->name ()); + + ACE_Stream_Type *st = + ACE_dynamic_cast (ACE_Stream_Type *, + ACE_const_cast (ACE_Service_Type_Impl *, + stream->record ()->type ())); + if (mt != 0 && st->remove (mt) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("cannot remove Module_Type %s from STREAM_Type %s\n"), + module->name (), + stream->name ())); + ACE_SVC_CONF_PARAM->yyerrno++; + } + ; + break;} +case 30: +{ + u_int flags + = ACE_Service_Type::DELETE_THIS + | (ace_yyvsp[-1].location_node_->dispose () == 0 ? 0 : ACE_Service_Type::DELETE_OBJ); + ACE_Service_Object_Exterminator gobbler = 0; + void *sym = ace_yyvsp[-1].location_node_->symbol (&gobbler); + + if (sym != 0) + { + ACE_Service_Type_Impl *stp + = ace_create_service_type (ace_yyvsp[-3].ident_, + ace_yyvsp[-2].type_, + sym, + flags, + gobbler); + ace_yyval.svc_record_ = new ACE_Service_Type (ace_yyvsp[-3].ident_, + stp, + ace_yyvsp[-1].location_node_->handle (), + ace_yyvsp[0].type_); + } + else + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("Unable to find service: %s\n"), + ace_yyvsp[-3].ident_)); + ++ACE_SVC_CONF_PARAM->yyerrno; + ace_yyval.svc_record_ = 0; + } + delete ace_yyvsp[-1].location_node_; + ; + break;} +case 31: +{ + ace_yyval.type_ = 1; + ; + break;} +case 32: +{ + ace_yyval.type_ = 0; + ; + break;} +case 33: +{ + ace_yyval.type_ = 1; + ; + break;} +case 34: +{ + ace_yyval.location_node_ = new ACE_Object_Node (ace_yyvsp[-2].ident_, ace_yyvsp[0].ident_); + ; + break;} +case 35: +{ + ace_yyval.location_node_ = new ACE_Function_Node (ace_yyvsp[-4].ident_, ace_yyvsp[-2].ident_); + ; + break;} +case 36: +{ + ace_yyval.location_node_ = new ACE_Static_Function_Node (ace_yyvsp[-2].ident_); + ; + break;} +case 37: +{ + ace_yyval.type_ = ACE_MODULE_T; + ; + break;} +case 38: +{ + ace_yyval.type_ = ACE_SVC_OBJ_T; + ; + break;} +case 39: +{ + ace_yyval.type_ = ACE_STREAM_T; + ; + break;} +case 41: +{ ace_yyval.ident_ = 0; ; + break;} +} + /* the action file gets copied in in place of this dollarsign */ + + + ace_yyvsp -= ace_yylen; + ace_yyssp -= ace_yylen; +#ifdef ACE_YYLSP_NEEDED + ace_yylsp -= ace_yylen; +#endif + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + { + short *ssp1 = ace_yyss - 1; + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("state stack now")); + while (ssp1 != ace_yyssp) + ACE_OS::fprintf (stderr, ACE_LIB_TEXT(" %d"), *++ssp1); + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("\n")); + } +#endif + + *++ace_yyvsp = ace_yyval; + +#ifdef ACE_YYLSP_NEEDED + ace_yylsp++; + if (ace_yylen == 0) + { + ace_yylsp->first_line = ace_yylloc.first_line; + ace_yylsp->first_column = ace_yylloc.first_column; + ace_yylsp->last_line = (ace_yylsp-1)->last_line; + ace_yylsp->last_column = (ace_yylsp-1)->last_column; + ace_yylsp->text = 0; + } + else + { + ace_yylsp->last_line = (ace_yylsp+ace_yylen-1)->last_line; + ace_yylsp->last_column = (ace_yylsp+ace_yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + ace_yyn = ace_yyr1[ace_yyn]; + + ace_yystate = ace_yypgoto[ace_yyn - ACE_YYNTBASE] + *ace_yyssp; + if (ace_yystate >= 0 && ace_yystate <= ACE_YYLAST && ace_yycheck[ace_yystate] == *ace_yyssp) + ace_yystate = ace_yytable[ace_yystate]; + else + ace_yystate = ace_yydefgoto[ace_yyn - ACE_YYNTBASE]; + + goto ace_yynewstate; + +ace_yyerrlab: /* here on detecting error */ + + if (! ace_yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++ace_yynerrs; + +#ifdef ACE_YYERROR_VERBOSE + ace_yyn = ace_yypact[ace_yystate]; + + if (ace_yyn > ACE_YYFLAG && ace_yyn < ACE_YYLAST) + { + int size = 0; + ACE_TCHAR *msg; + int x, count; + + count = 0; + /* Start X at -ace_yyn if nec to avoid negative indexes in ace_yycheck. */ + for (x = (ace_yyn < 0 ? -ace_yyn : 0); + x < (sizeof(ace_yytname) / sizeof(ACE_TCHAR *)); x++) + if (ace_yycheck[x + ace_yyn] == x) + size += ACE_OS::strlen(ace_yytname[x]) + 15, count++; + msg = new ACE_TCHAR[size + 15]; + if (msg != 0) + { + ACE_OS::strcpy(msg, ACE_LIB_TEXT("parse error")); + + if (count < 5) + { + count = 0; + for (x = (ace_yyn < 0 ? -ace_yyn : 0); + x < (sizeof(ace_yytname) / sizeof(ACE_TCHAR *)); x++) + if (ace_yycheck[x + ace_yyn] == x) + { + ACE_OS::strcat(msg, count == 0 ? ACE_LIB_TEXT(", expecting `") : ACE_LIB_TEXT(" or `")); + ACE_OS::strcat(msg, ACE_TEXT_CHAR_TO_TCHAR (ace_yytname[x])); + ACE_OS::strcat(msg, ACE_LIB_TEXT("'")); + count++; + } + } + ace_yyerror(msg); + delete [] msg; + } + else + ace_yyerror (ACE_LIB_TEXT("parse error; also virtual memory exceeded")); + } + else +#endif /* ACE_YYERROR_VERBOSE */ + ace_yyerror(ACE_LIB_TEXT("parse error")); + } + + goto ace_yyerrlab1; +ace_yyerrlab1: /* here on error raised explicitly by an action */ + + if (ace_yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (ace_yychar == ACE_YYEOF) + ACE_YYABORT; + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Discarding token %d (%s).\n"), ace_yychar, ace_yytname[ace_yychar1]); +#endif + + ace_yychar = ACE_YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + ace_yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto ace_yyerrhandle; + +ace_yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + ace_yyn = ace_yydefact[ace_yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (ace_yyn) goto ace_yydefault; +#endif + +ace_yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (ace_yyssp == ace_yyss) ACE_YYABORT; + ace_yyvsp--; + ace_yystate = *--ace_yyssp; +#ifdef ACE_YYLSP_NEEDED + ace_yylsp--; +#endif + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + { + short *ssp1 = ace_yyss - 1; + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("Error: state stack now")); + while (ssp1 != ace_yyssp) + ACE_OS::fprintf (stderr, ACE_LIB_TEXT(" %d"), *++ssp1); + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("\n")); + } +#endif + +ace_yyerrhandle: + + ace_yyn = ace_yypact[ace_yystate]; + if (ace_yyn == ACE_YYFLAG) + goto ace_yyerrdefault; + + ace_yyn += ACE_YYTERROR; + if (ace_yyn < 0 || ace_yyn > ACE_YYLAST || ace_yycheck[ace_yyn] != ACE_YYTERROR) + goto ace_yyerrdefault; + + ace_yyn = ace_yytable[ace_yyn]; + if (ace_yyn < 0) + { + if (ace_yyn == ACE_YYFLAG) + goto ace_yyerrpop; + ace_yyn = -ace_yyn; + goto ace_yyreduce; + } + else if (ace_yyn == 0) + goto ace_yyerrpop; + + if (ace_yyn == ACE_YYFINAL) + ACE_YYACCEPT; + +#if ACE_YYDEBUG != 0 + if (ace_yydebug) + ACE_OS::fprintf(stderr, ACE_LIB_TEXT("Shifting error token, ")); +#endif + + *++ace_yyvsp = ace_yylval; +#ifdef ACE_YYLSP_NEEDED + *++ace_yylsp = ace_yylloc; +#endif + + ace_yystate = ace_yyn; + goto ace_yynewstate; + + ace_yyacceptlab: + /* ACE_YYACCEPT comes here. */ + if (ace_yyfree_stacks) + { + free (ace_yyss); + free (ace_yyvs); +#ifdef ACE_YYLSP_NEEDED + free (ace_yyls); +#endif + } + return 0; + + ace_yyabortlab: + /* ACE_YYABORT comes here. */ + if (ace_yyfree_stacks) + { + free (ace_yyss); + free (ace_yyvs); +#ifdef ACE_YYLSP_NEEDED + free (ace_yyls); +#endif + } + return 1; +} + +// Prints the error string to standard output. Cleans up the error +// messages. + +void +ace_yyerror (const ACE_TCHAR *s) +{ +#if defined (ACE_NLOGGING) + ACE_UNUSED_ARG (s); +#endif /* ACE_NLOGGING */ + + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("[error %d] on line %d: %s\n"), + ace_yyerrno, + ace_yylineno, + s)); +} + +// Note that SRC_REC represents left context, which is the STREAM * +// record. + +static ACE_Module_Type * +ace_get_module (ACE_Static_Node *str_rec, + const ACE_TCHAR *svc_name) +{ + const ACE_Service_Type *sr = str_rec->record (); + const ACE_Service_Type_Impl *type = sr->type (); + ACE_Stream_Type *st = sr == 0 + ? 0 + : ACE_dynamic_cast (ACE_Stream_Type *, + ACE_const_cast (ACE_Service_Type_Impl *, + type)); + ACE_Module_Type *mt = st == 0 ? 0 : st->find (svc_name); + + if (sr == 0 || st == 0 || mt == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("cannot locate Module_Type %s in STREAM_Type %s\n"), + svc_name, + str_rec->name ())); + ace_yyerrno++; + } + + return mt; +} + +static ACE_Module_Type * +ace_get_module (ACE_Static_Node *str_rec, + ACE_Static_Node *svc_type) +{ + const ACE_Service_Type *sr = str_rec->record (); + const ACE_Service_Type_Impl *type = sr->type (); + ACE_Stream_Type *st = sr == 0 ? 0 : (ACE_Stream_Type *) type; + const ACE_Service_Type *sv = svc_type->record (); + type = sv->type (); + ACE_Module_Type *mt = (ACE_Module_Type *) type; + const ACE_TCHAR *module_type_name = svc_type->name (); + + if (sr == 0 || st == 0 || mt == 0) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("cannot locate Module_Type %s or STREAM_Type %s\n"), + module_type_name, + str_rec->name ())); + ace_yyerrno++; + } + + // Make sure that the Module has the same name as the + // Module_Type object from the svc.conf file. + ACE_Module *mp = (ACE_Module *) mt->object (); + + if (ACE_OS::strcmp (mp->name (), module_type_name) != 0) + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("warning: assigning Module_Type name %s to Module %s since names differ\n"), + module_type_name, + mp->name ())); + mp->name (module_type_name); + } + + return mt; +} + +ACE_Service_Type_Impl * +ace_create_service_type (const ACE_TCHAR *name, + int type, + void *symbol, + u_int flags, + ACE_Service_Object_Exterminator gobbler) +{ + ACE_Service_Type_Impl *stp = 0; + + // Note, the only place we need to put a case statement. This is + // also the place where we'd put the RTTI tests, if the compiler + // actually supported them! + + switch (type) + { + case ACE_SVC_OBJ_T: + ACE_NEW_RETURN (stp, + ACE_Service_Object_Type ((ACE_Service_Object *) symbol, + name, flags, + gobbler), + 0); + break; + case ACE_MODULE_T: + ACE_NEW_RETURN (stp, + ACE_Module_Type (symbol, name, flags), + 0); + break; + case ACE_STREAM_T: + ACE_NEW_RETURN (stp, + ACE_Stream_Type (symbol, name, flags), + 0); + break; + default: + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("unknown case\n"))); + ace_yyerrno++; + break; + } + return stp; +} + +#if defined (DEBUGGING) +// Current line number. +int ace_yylineno = 1; + +// Name given on the command-line to envoke the program. +ACE_TCHAR *program_name; + +// Main driver program. + +int +main (int argc, ACE_TCHAR *argv[]) +{ + ACE_Svc_Conf_Param param (stdin); + + // Try to reopen any filename argument to use ACE_YYIN. + if (argc > 1 && (ace_yyin = freopen (argv[1], ACE_LIB_TEXT("r"), stdin)) == 0) + ACE_OS::fprintf (stderr, ACE_LIB_TEXT("usage: %s [file]\n"), argv[0]), ACE_OS::exit (1); + + return ace_yyparse (¶m); +} +#endif /* DEBUGGING */ diff --git a/ace/Svcconf/Svc_Handler.cpp b/ace/Svcconf/Svc_Handler.cpp new file mode 100644 index 00000000000..9614a404e12 --- /dev/null +++ b/ace/Svcconf/Svc_Handler.cpp @@ -0,0 +1,512 @@ +// $Id$ + +#ifndef ACE_SVC_HANDLER_C +#define ACE_SVC_HANDLER_C + +#include "ace/Svc_Handler.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Object_Manager.h" +#include "ace/Connection_Recycling_Strategy.h" + +#include "ace/Dynamic.h" + +ACE_RCSID(ace, Svc_Handler, "$Id$") + +#define PR_ST_1 ACE_PEER_STREAM_1 +#define PR_ST_2 ACE_PEER_STREAM_2 + +template void * +ACE_Svc_Handler::operator new (size_t, + void *p) +{ + ACE_TRACE ("ACE_Svc_Handler::operator new (NOOP, 2 parameters)"); + return p; +} + +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) +template void +ACE_Svc_Handler::operator delete (void *, + void *) +{ + ACE_TRACE ("ACE_Svc_Handler::operator delete (NOOP, 2 parameters)"); + return; +} +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ + +template void * +ACE_Svc_Handler::operator new (size_t n) +{ + ACE_TRACE ("ACE_Svc_Handler::operator new"); + + 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) +template void * +ACE_Svc_Handler::operator new (size_t n, + const nothrow_t&) +{ + ACE_TRACE ("ACE_Svc_Handler::operator new(nothrow)"); + + 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(nothrow) char[n]; + } +} +#endif /* ACE_HAS_NEW_NOTHROW */ + +template void +ACE_Svc_Handler::destroy (void) +{ + ACE_TRACE ("ACE_Svc_Handler::destroy"); + + // Only delete ourselves if we're not owned by a module and have + // been allocated dynamically. + if (this->mod_ == 0 && this->dynamic_ && this->closing_ == 0) + // Will call the destructor, which automatically calls . + // Note that if we are *not* allocated dynamically then the + // destructor will call automatically when it gets run + // during cleanup. + delete this; +} + +template void +ACE_Svc_Handler::operator delete (void *obj) +{ + ACE_TRACE ("ACE_Svc_Handler::operator delete"); + // You cannot delete a 'void*' (X3J16/95-0087 5.3.5.3), but we know + // the pointer was created using new char[] (see operator new code), + // so we use a cast: + char *tmp = (char *) obj; + ::delete [] tmp; +} + +// Default constructor. + +template +ACE_Svc_Handler::ACE_Svc_Handler (ACE_Thread_Manager *tm, + ACE_Message_Queue *mq, + ACE_Reactor *reactor) + : ACE_Task (tm, mq), + closing_ (0), + recycler_ (0), + recycling_act_ (0) +{ + ACE_TRACE ("ACE_Svc_Handler::ACE_Svc_Handler"); + + this->reactor (reactor); + + // This clever idiom transparently checks if we were allocated + // dynamically. This information is used by the method to + // decide if we need to delete ... The idiom is based on a + // paper by Michael van Rooyen (mrooyen@cellnet.co.uk) that appeared + // in the April '96 issue of the C++ Report. We've spruced it up to + // work correctly in multi-threaded programs by using our ACE_TSS + // class. + this->dynamic_ = ACE_Dynamic::instance ()->is_dynamic (); + + if (this->dynamic_ != 0) + // Make sure to reset the flag. + ACE_Dynamic::instance ()->reset (); +} + +// Default behavior for a ACE_Svc_Handler object is to be registered +// with the ACE_Reactor (thereby ensuring single threading). + +template int +ACE_Svc_Handler::open (void *) +{ + ACE_TRACE ("ACE_Svc_Handler::open"); +#if defined (ACE_DEBUGGING) + ACE_TCHAR buf[BUFSIZ]; + ACE_PEER_STREAM_ADDR client_addr; + + if (this->peer_.get_remote_addr (client_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("get_remote_addr")), + -1); + else if (client_addr.addr_to_string (buf, sizeof buf) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("can't obtain peer's address")), + -1); + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("connected to %s on fd %d\n"), + buf, + this->peer_.get_handle ())); +#endif /* ACE_DEBUGGING */ + if (this->reactor () + && this->reactor ()->register_handler + (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("unable to register client handler")), + -1); + return 0; +} + +// Perform termination activities. + +template void +ACE_Svc_Handler::shutdown (void) +{ + ACE_TRACE ("ACE_Svc_Handler::shutdown"); + + // Deregister this handler with the ACE_Reactor. + if (this->reactor ()) + { + ACE_Reactor_Mask mask = ACE_Event_Handler::ALL_EVENTS_MASK | + ACE_Event_Handler::DONT_CALL; + + // Make sure there are no timers. + this->reactor ()->cancel_timer (this); + + if (this->peer ().get_handle () != ACE_INVALID_HANDLE) + // Remove self from reactor. + this->reactor ()->remove_handler (this, mask); + } + + // Remove self from the recycler. + if (this->recycler ()) + this->recycler ()->purge (this->recycling_act_); + + this->peer ().close (); +} + +template void +ACE_Svc_Handler::cleanup_hint (void **act_holder) +{ + ACE_TRACE ("ACE_Svc_Handler::cleanup_hint"); + + // Remove as hint. + if (this->recycler ()) + this->recycler ()->cleanup_hint (this->recycling_act_, + act_holder); +} + +template void +ACE_Svc_Handler::dump (void) const +{ + ACE_TRACE ("ACE_Svc_Handler::dump"); + + this->peer_.dump (); + ACE_DEBUG ((LM_DEBUG, + "dynamic_ = %d\n", + this->dynamic_)); + ACE_DEBUG ((LM_DEBUG, + "closing_ = %d\n", + this->closing_)); + ACE_DEBUG ((LM_DEBUG, + "recycler_ = %d\n", + this->recycler_)); + ACE_DEBUG ((LM_DEBUG, + "recycling_act_ = %d\n", + this->recycling_act_)); +} + +template ACE_PEER_STREAM & +ACE_Svc_Handler::peer (void) const +{ + ACE_TRACE ("ACE_Svc_Handler::peer"); + return (ACE_PEER_STREAM &) this->peer_; +} + +// Extract the underlying I/O descriptor. + +template ACE_HANDLE +ACE_Svc_Handler::get_handle (void) const +{ + ACE_TRACE ("ACE_Svc_Handler::get_handle"); + return this->peer_.get_handle (); +} + +// Set the underlying I/O descriptor. + +template void +ACE_Svc_Handler::set_handle (ACE_HANDLE h) +{ + ACE_TRACE ("ACE_Svc_Handler::set_handle"); + this->peer_.set_handle (h); +} + +template +ACE_Svc_Handler::~ACE_Svc_Handler (void) +{ + ACE_TRACE ("ACE_Svc_Handler::~ACE_Svc_Handler"); + + if (this->closing_ == 0) + { + // We're closing down now, so make sure not to call ourselves + // recursively via other calls to handle_close() (e.g., from the + // Timer_Queue). + this->closing_ = 1; + + this->shutdown (); + } +} + +template int +ACE_Svc_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_TRACE ("ACE_Svc_Handler::handle_close"); + + this->destroy (); + return 0; +} + +template int +ACE_Svc_Handler::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_TRACE ("ACE_Svc_Handler::handle_timeout"); + return this->handle_close (); +} + +template int +ACE_Svc_Handler::close (unsigned long) +{ + ACE_TRACE ("ACE_Svc_Handler::close"); + return this->handle_close (); +} + +template int +ACE_Svc_Handler::init (int argc, ACE_TCHAR *argv[]) +{ + ACE_TRACE ("ACE_Svc_Handler::init"); + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + return -1; +} + +template int +ACE_Svc_Handler::fini (void) +{ + ACE_TRACE ("ACE_Svc_Handler::fini"); + return -1; +} + +template int +ACE_Svc_Handler::info (ACE_TCHAR **, size_t) const +{ + ACE_TRACE ("ACE_Svc_Handler::info"); + return -1; +} + +template int +ACE_Svc_Handler::idle (u_long flags) +{ + if (this->recycler ()) + return this->recycler ()->cache (this->recycling_act_); + else + return this->close (flags); +} + +template int +ACE_Svc_Handler::recycle_state (ACE_Recyclable_State new_state) +{ + if (this->recycler ()) + return this->recycler ()->recycle_state (this->recycling_act_, + new_state); + + return 0; +} + +template ACE_Recyclable_State +ACE_Svc_Handler::recycle_state (void) const +{ + if (this->recycler ()) + return this->recycler ()->recycle_state (this->recycling_act_); + + return ACE_RECYCLABLE_UNKNOWN; +} + +template void +ACE_Svc_Handler::recycler (ACE_Connection_Recycling_Strategy *recycler, + const void *recycling_act) +{ + ACE_TRACE ("ACE_Svc_Handler::recycler"); + this->recycler_ = recycler; + this->recycling_act_ = recycling_act; +} + +template ACE_Connection_Recycling_Strategy * +ACE_Svc_Handler::recycler (void) const +{ + ACE_TRACE ("ACE_Svc_Handler::recycler"); + return this->recycler_; +} + +template const void * +ACE_Svc_Handler::recycling_act (void) const +{ + ACE_TRACE ("ACE_Svc_Handler::recycling_act"); + return this->recycling_act_; +} + +template int +ACE_Svc_Handler::recycle (void *) +{ + ACE_TRACE ("ACE_Svc_Handler::recycle"); + // By default, the object is ready and willing to be recycled. + return 0; +} + +template +ACE_Buffered_Svc_Handler::~ACE_Buffered_Svc_Handler (void) +{ + this->flush (); +} + +template +ACE_Buffered_Svc_Handler::ACE_Buffered_Svc_Handler (ACE_Thread_Manager *tm, + ACE_Message_Queue *mq, + ACE_Reactor *reactor, + size_t maximum_buffer_size, + ACE_Time_Value *timeout) + : ACE_Svc_Handler (tm, mq, reactor), + maximum_buffer_size_ (maximum_buffer_size), + current_buffer_size_ (0), + timeoutp_ (timeout) +{ + ACE_TRACE ("ACE_Buffered_Svc_Handler::ACE_Buffered_Svc_Handler"); + + if (this->timeoutp_ != 0) + { + this->interval_ = *timeout; + this->next_timeout_ = ACE_OS::gettimeofday () + this->interval_; + } +} + +template int +ACE_Buffered_Svc_Handler::put (ACE_Message_Block *mb, + ACE_Time_Value *tv) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->msg_queue ()->lock (), -1); + + // Enqueue onto the message queue. + if (this->putq (mb, tv) == -1) + return -1; + else + { + // Update the current number of bytes on the queue. + this->current_buffer_size_ += mb->total_size (); + + // Flush the buffer when the number of bytes exceeds the maximum + // buffer size or when the timeout period has elapsed. + if (this->current_buffer_size_ >= this->maximum_buffer_size_ + || (this->timeoutp_ != 0 + && this->next_timeout_ <= ACE_OS::gettimeofday ())) + return this->flush (); + else + return 0; + } +} + +// Flush the buffer. + +template int +ACE_Buffered_Svc_Handler::flush (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX_T, m, this->msg_queue ()->lock (), -1); + + ACE_Message_Queue_Iterator iterator (*this->msg_queue ()); + ACE_Message_Block *mblk; + int result = 0; + + // Get the first so that we can write everything + // out via the . + if (iterator.next (mblk) != 0) + result = this->peer ().send_n (mblk); + + // Remove all the s in the + // and their memory. + while (this->msg_queue ()->is_empty () == 0) + { + if (this->msg_queue ()->dequeue_head (mblk) == -1) + break; + + mblk->release (); + } + + if (this->timeoutp_ != 0) + // Update the next timeout period by adding the interval. + this->next_timeout_ += this->interval_; + + this->current_buffer_size_ = 0; + + return result; +} + +template void +ACE_Buffered_Svc_Handler::dump (void) const +{ + ACE_TRACE ("ACE_Buffered_Svc_Handler::dump"); + + ACE_Buffered_Svc_Handler::dump (); + ACE_DEBUG ((LM_DEBUG, + "maximum_buffer_size_ = %d\n", + this->maximum_buffer_size_)); + ACE_DEBUG ((LM_DEBUG, + "current_buffer_size_ = %d\n", + this->current_buffer_size_)); + if (this->timeoutp_ != 0) + ACE_DEBUG ((LM_DEBUG, + "next_timeout_.sec = %d, next_timeout_.usec = %d\n", + this->next_timeout_.sec (), + this->next_timeout_.usec ())); + else + ACE_DEBUG ((LM_DEBUG, + "timeoutp_ == NULL")); +} + +template int +ACE_Buffered_Svc_Handler::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_TRACE ("ACE_Buffered_Svc_Handler::handle_timeout"); + return 0; +} + +#undef PR_ST_1 +#undef PR_ST_2 +#endif /* ACE_SVC_HANDLER_C */ diff --git a/ace/Svcconf/Svc_Handler.h b/ace/Svcconf/Svc_Handler.h new file mode 100644 index 00000000000..054c950b7a1 --- /dev/null +++ b/ace/Svcconf/Svc_Handler.h @@ -0,0 +1,322 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Svc_Handler.h + * + * $Id$ + * + * @author Douglas Schmidt and + * Irfan Pyrarli + */ +//============================================================================= + +#ifndef ACE_SVC_HANDLER_H +#define ACE_SVC_HANDLER_H +#include "ace/pre.h" + +// Forward decls. +class ACE_Connection_Recycling_Strategy; + +#include "ace/Synch_Options.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Task.h" +#include "ace/Service_Config.h" +#include "ace/Recyclable.h" + +/** + * @class ACE_Svc_Handler + * + * @brief Defines the interface for a service that exchanges data with + * its connected peer. + * + * This class provides a well-defined interface that the + * Acceptor and Connector pattern factories use as their target. + * Typically, client applications will subclass ACE_Svc_Handler + * and do all the interesting work in the subclass. One thing + * that the ACE_Svc_Handler does contain is a PEER_STREAM + * endpoint that is initialized by an ACE_Acceptor or + * ACE_Connector when a connection is established successfully. + * This endpoint is used to exchange data between a + * ACE_Svc_Handler and the peer it is connected with. + */ +template +class ACE_Svc_Handler : public ACE_Task +{ +public: + // = Initialization and termination methods. + /** + * Constructor initializes the and by passing them + * down to the base class. The is passed to + * the . + */ + ACE_Svc_Handler (ACE_Thread_Manager *thr_mgr = 0, + ACE_Message_Queue *mq = 0, + ACE_Reactor *reactor = ACE_Reactor::instance ()); + + /// Destructor. + virtual ~ACE_Svc_Handler (void); + + /// Activate the client handler. This is typically called by the + /// or . + virtual int open (void * = 0); + + /** + * Object termination hook -- application-specific cleanup code goes + * here. This function is called by the idle() function if the object + * does not have a ACE_Connection_Recycling_Strategy associated with it. + * Also, due to this class's derivation from , is + * also called when a thread activated with this object exits. See + * for further details. The default action of this + * function is to call with the default arguments. + */ + virtual int close (u_long flags = 0); + + /** + * Call this method if you want to recycling the + * instead of closing it. If the object does not have a recycler, + * it will be closed. + */ + virtual int idle (u_long flags = 0); + + /** + * Call this method if you want to get/set the state of the + * . If the object does not have a recycler, this call + * will have no effect (and the accessor will return + * ACE_RECYCLABLE_UNKNOWN). + */ + virtual ACE_Recyclable_State recycle_state (void) const; + virtual int recycle_state (ACE_Recyclable_State new_state); + + /** + * When the svc_handle is no longer needed around as a hint, call + * this method. In addition, reset <*act_holder> to zero if + * . + */ + virtual void cleanup_hint (void **act_holder = 0); + + // = Dynamic linking hooks. + /// Default version does no work and returns -1. Must be overloaded + /// by application developer to do anything meaningful. + virtual int init (int argc, ACE_TCHAR *argv[]); + + /// Default version does no work and returns -1. Must be overloaded + /// by application developer to do anything meaningful. + virtual int fini (void); + + /// Default version does no work and returns -1. Must be overloaded + /// by application developer to do anything meaningful. + virtual int info (ACE_TCHAR **info_string, size_t length) const; + + // = Demultiplexing hooks. + + /** + * Perform termination activities on the SVC_HANDLER. The default + * behavior is to close down the (to avoid descriptor leaks) + * and to this object (to avoid memory leaks)! If you + * don't want this behavior make sure you override this method... + */ + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); + + /// Default behavior when timeouts occur is to close down the + /// by calling . + virtual int handle_timeout (const ACE_Time_Value &time, + const void *); + + /// Get the underlying handle associated with the . + virtual ACE_HANDLE get_handle (void) const; + + /// Set the underlying handle associated with the . + virtual void set_handle (ACE_HANDLE); + + /// Returns the underlying PEER_STREAM. Used by + /// and factories + ACE_PEER_STREAM &peer (void) const; + + /// Overloaded new operator. This method unobtrusively records if a + /// is allocated dynamically, which allows it to clean + /// itself up correctly whether or not it's allocated statically or + /// dynamically. + void *operator new (size_t n); + +#if defined (ACE_HAS_NEW_NOTHROW) + /// Overloaded new operator, nothrow_t variant. Unobtrusively records if a + /// is allocated dynamically, which allows it to clean + /// itself up correctly whether or not it's allocated statically or + /// dynamically. + void *operator new (size_t n, const nothrow_t&); +#endif + + /// This operator permits "placement new" on a per-object basis. + void * operator new (size_t n, + void *p); + + /** + * Call this to free up dynamically allocated + * (otherwise you will get memory leaks). In general, you should + * call this method rather than since this method knows + * whether or not the object was allocated dynamically, and can act + * accordingly (i.e., deleting it if it was allocated dynamically). + */ + virtual void destroy (void); + + /** + * This really should be private so that users are forced to call + * . Unfortunately, the C++ standard doesn't allow there + * to be a public new and a private delete. It is a bad idea to + * call this method directly, so use instead, unless you + * know for sure that you've allocated the object dynamically. + */ + void operator delete (void *); + +#if !defined (ACE_LACKS_PLACEMENT_OPERATOR_DELETE) + /** + * This operator is necessary to complement the class-specific + * operator new above. Unfortunately, it's not portable to all C++ + * compilers... + */ + void operator delete (void *, void *); +#endif /* ACE_LACKS_PLACEMENT_OPERATOR_DELETE */ + + /// Close down the descriptor and unregister from the Reactor + void shutdown (void); + + /// Dump the state of an object. + void dump (void) const; + +public: + + // = The following methods are not suppose to be public. + + // Because friendship is *not* inherited in C++, these methods have + // to be public. + + // = Accessors to set/get the connection recycler. + + /// Set the recycler and the that is used during + /// purging and caching. + virtual void recycler (ACE_Connection_Recycling_Strategy *recycler, + const void *recycling_act); + + /// Get the recycler. + virtual ACE_Connection_Recycling_Strategy *recycler (void) const; + + /// Get the recycling act. + virtual const void *recycling_act (void) const; + + /** + * Upcall made by the recycler when it is about to recycle the + * connection. This gives the object a chance to prepare itself for + * recycling. Return 0 if the object is ready for recycling, -1 on + * failures. + */ + virtual int recycle (void * = 0); + +protected: + /// Maintain connection with client. + ACE_PEER_STREAM peer_; + + /// Have we been dynamically created? + int dynamic_; + + /// Keeps track of whether we are in the process of closing (required + /// to avoid circular calls to ). + char closing_; + + /// Pointer to the connection recycler. + ACE_Connection_Recycling_Strategy *recycler_; + + /// Asynchronous Completion Token (ACT) to be used to when talking to + /// the recycler. + const void *recycling_act_; +}; + +/** + * @class ACE_Buffered_Svc_Handler + * + * @brief Defines the interface for a service that exchanges data with + * its connected peer and supports buffering. + * + * The buffering feature makes it possible to queue up + * in an until (1) the + * queue is "full" or (2) a period of time elapses, at which + * point the queue is "flushed" via to the peer. + */ +template +class ACE_Buffered_Svc_Handler : public ACE_Svc_Handler +{ +public: + // = Initialization and termination methods. + /** + * Constructor initializes the and by passing them + * down to the base class. The is passed to + * the . The and + * are used to determine at what point to flush + * the . By default, there's no buffering at all. The + * value is interpreted to be in a unit that's + * relative to the current time returned by . + */ + ACE_Buffered_Svc_Handler (ACE_Thread_Manager *thr_mgr = 0, + ACE_Message_Queue *mq = 0, + ACE_Reactor *reactor = ACE_Reactor::instance (), + size_t max_buffer_size = 0, + ACE_Time_Value *relative_timeout = 0); + + /// Destructor, which calls . + virtual ~ACE_Buffered_Svc_Handler (void); + + /** + * Insert the chain rooted at + * into the with the designated . The + * method will be called if this causes the number of + * bytes to exceed the maximum buffer size or if the timeout period + * has elapsed. + */ + virtual int put (ACE_Message_Block *message_block, + ACE_Time_Value *timeout = 0); + + /// Flush the , which writes all the queued + /// s to the . + virtual int flush (void); + + /// This method is not currently implemented -- this is where the + /// integration with the would occur. + virtual int handle_timeout (const ACE_Time_Value &time, + const void *); + + /// Dump the state of an object. + void dump (void) const; + +protected: + /// Maximum size the can be before we have to flush + /// the buffer. + size_t maximum_buffer_size_; + + /// Current size in bytes of the contents. + size_t current_buffer_size_; + + /// Timeout value used to control when the buffer is flushed. + ACE_Time_Value next_timeout_; + + /// Interval of the timeout. + ACE_Time_Value interval_; + + /// Timeout pointer. + ACE_Time_Value *timeoutp_; +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Svc_Handler.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Svc_Handler.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_SVC_HANDLER_H */ diff --git a/ace/Svcconf/svc_export.h b/ace/Svcconf/svc_export.h new file mode 100644 index 00000000000..b10b4f33bc5 --- /dev/null +++ b/ace/Svcconf/svc_export.h @@ -0,0 +1,44 @@ +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. + +// This file was generated by generate_export_file.pl +// but needed to be altered to support ACE_BUILD_SVC_DLL +// instead of ACE_SVC_BUILD_DLL which was already being +// used. + +// ------------------------------ +#if !defined (ACE_SVC_EXPORT_H) +#define ACE_SVC_EXPORT_H + +#include "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (ACE_SVC_HAS_DLL) +# define ACE_SVC_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && ACE_SVC_HAS_DLL */ + +#if !defined (ACE_SVC_HAS_DLL) +#define ACE_SVC_HAS_DLL 1 +#endif /* ! ACE_SVC_HAS_DLL */ + +#if defined (ACE_SVC_HAS_DLL) +# if (ACE_SVC_HAS_DLL == 1) +# if defined (ACE_BUILD_SVC_DLL) || defined (ACE_SVC_BUILD_DLL) +# define ACE_Svc_Export ACE_Proper_Export_Flag +# define ACE_SVC_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# else +# define ACE_Svc_Export ACE_Proper_Import_Flag +# define ACE_SVC_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# endif /* ACE_BUILD_SVC_DLL */ +# else +# define ACE_Svc_Export +# define ACE_SVC_SINGLETON_DECLARATION(T) +# endif /* ! ACE_SVC_HAS_DLL == 1 */ +#else +# define ACE_Svc_Export +# define ACE_SVC_SINGLETON_DECLARATION(T) +#endif /* ACE_SVC_HAS_DLL */ + +#endif /* ACE_SVC_EXPORT_H */ + +// End of auto generated file. diff --git a/ace/Threads/Activation_Queue.cpp b/ace/Threads/Activation_Queue.cpp new file mode 100644 index 00000000000..0463b8178a7 --- /dev/null +++ b/ace/Threads/Activation_Queue.cpp @@ -0,0 +1,109 @@ +// $Id$ + +#include "ace/Threads/Activation_Queue.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Threads/Activation_Queue.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Activation_Queue, "$Id$") + +// Dump the state of an object. + +void +ACE_Activation_Queue::dump (void) const +{ + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("delete_queue_ = %d\n"), + this->delete_queue_)); + ACE_DEBUG ((LM_INFO, ACE_LIB_TEXT ("queue_: \n"))); + if (this->queue_) + this->queue_->dump(); + else + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("(NULL)\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Activation_Queue::ACE_Activation_Queue (ACE_Message_Queue *new_queue, + ACE_Allocator *alloc, + ACE_Allocator *db_alloc) + : delete_queue_ (0) + , allocator_(alloc) + , data_block_allocator_(db_alloc) +{ + if (this->allocator_ == 0) + this->allocator_ = ACE_Allocator::instance (); + + if (new_queue) + this->queue_ = new_queue; + else + { + ACE_NEW (this->queue_, + ACE_Message_Queue); + this->delete_queue_ = 1; + } +} + +ACE_Activation_Queue::~ACE_Activation_Queue (void) +{ + if (this->delete_queue_ != 0) + delete this->queue_; +} + +ACE_Method_Request * +ACE_Activation_Queue::dequeue (ACE_Time_Value *tv) +{ + ACE_Message_Block *mb; + + // Dequeue the message. + if (this->queue_->dequeue_head (mb, tv) != -1) + { + // Get the next . + ACE_Method_Request *mr = + ACE_reinterpret_cast (ACE_Method_Request *, + mb->base ()); + // Delete the message block. + mb->release (); + return mr; + } + else + return 0; +} + +int +ACE_Activation_Queue::enqueue (ACE_Method_Request *mr, + ACE_Time_Value *tv) +{ + ACE_Message_Block *mb; + + // We pass sizeof (*mr) here so that flow control will work + // correctly. Since we also pass note that no unnecessary + // memory is actually allocated -- just the size field is set. + ACE_NEW_MALLOC_RETURN (mb, + ACE_static_cast(ACE_Message_Block *, + this->allocator_->malloc (sizeof (ACE_Message_Block))), + ACE_Message_Block (sizeof (*mr), // size + ACE_Message_Block::MB_DATA, // type + 0, // cont + (char *) mr, // data + 0, // allocator + 0, // locking strategy + mr->priority (), // priority + ACE_Time_Value::zero, // execution time + ACE_Time_Value::max_time, // absolute time of deadline + this->data_block_allocator_, // data_block allocator + this->allocator_), // message_block allocator + -1); + + // Enqueue in priority order. + int result = this->queue_->enqueue_prio (mb, tv); + + // Free ACE_Message_Block if enqueue_prio failed. + if (result == -1) + ACE_DES_FREE (mb, this->allocator_->free, ACE_Message_Block); + + return result; +} + + diff --git a/ace/Threads/Activation_Queue.h b/ace/Threads/Activation_Queue.h new file mode 100644 index 00000000000..c6fe043c502 --- /dev/null +++ b/ace/Threads/Activation_Queue.h @@ -0,0 +1,109 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Activation_Queue.h + * + * $Id$ + * + * @author Andres Kruse + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_ACTIVATION_QUEUE_H +#define ACE_ACTIVATION_QUEUE_H +#include "ace/pre.h" + +#include "ace/Threads/Synch_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Threads/Message_Queue.h" +#include "ace/Method_Request.h" + +// Be compatible with the terminology in the POSA2 book! +#define ACE_Activation_List ACE_Activation_Queue + +/** + * @class ACE_Activation_Queue + * + * @brief Reifies a method into a request. Subclasses typically + * represent necessary state and behavior. + * + * A is inserted in the , + * where it is subsequently removed by the , which + * invokes its method.. + */ +class ACE_Export ACE_Activation_Queue +{ +public: + // = Initialization and termination methods. + /// Constructor. + ACE_Activation_Queue (ACE_Message_Queue *new_queue = 0, + ACE_Allocator *alloc = 0, + ACE_Allocator *db_alloc = 0); + + /// Destructor. + virtual ~ACE_Activation_Queue (void); + + // = Activate Queue operations. + + // For the following two methods if == 0, the caller will + // block until action is possible, else will wait until the absolute + // time specified in * elapses. These calls will return, + // however, when queue is closed, deactivated, when a signal occurs, + // or if the time specified in timeout elapses, (in which case errno + // = EWOULDBLOCK). + + /// Dequeue the next available . + ACE_Method_Request *dequeue (ACE_Time_Value *tv = 0); + + /// Enqueue the in priority order. The priority is + /// determined by the method of the . + int enqueue (ACE_Method_Request *new_method_request, + ACE_Time_Value *tv = 0); + + /// Get the current number of method objects in the queue. + int method_count (void) const; + + /// Returns 1 if the queue is empty, 0 otherwise. + int is_empty (void) const; + + /// Returns 1 if the queue is full, 0 otherwise. + int is_full (void) const; + + /// Dump the state of an request. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Stores the . + ACE_Message_Queue *queue_; + + /// Keeps track of whether we need to delete the queue. + int delete_queue_; + +private: + /// Allocation strategy of the queue. + ACE_Allocator *allocator_; + + /// Allocation strategy of the message blocks. + ACE_Allocator *data_block_allocator_; + + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Activation_Queue &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Activation_Queue (const ACE_Activation_Queue &)) +}; + +#if defined (__ACE_INLINE__) +#include "ace/Activation_Queue.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_ACTIVATION_QUEUE_H */ + diff --git a/ace/Threads/Activation_Queue.i b/ace/Threads/Activation_Queue.i new file mode 100644 index 00000000000..0a70b9f6c49 --- /dev/null +++ b/ace/Threads/Activation_Queue.i @@ -0,0 +1,22 @@ +/* -*- C++ -*- */ +// $Id$ + +// Activation_Queue.i + +ACE_INLINE int +ACE_Activation_Queue::method_count (void) const +{ + return queue_->message_count (); +} + +ACE_INLINE int +ACE_Activation_Queue::is_full (void) const +{ + return queue_->is_full (); +} + +ACE_INLINE int +ACE_Activation_Queue::is_empty (void) const +{ + return queue_->is_empty (); +} diff --git a/ace/Threads/Atomic_Op.cpp b/ace/Threads/Atomic_Op.cpp new file mode 100644 index 00000000000..df82902efcb --- /dev/null +++ b/ace/Threads/Atomic_Op.cpp @@ -0,0 +1,83 @@ +#ifndef ACE_ATOMIC_OP_C +#define ACE_ATOMIC_OP_C + +#include "ace/Atomic_Op.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if !defined (__ACE_INLINE__) +// On non-Win32 platforms, this code will be treated as normal code. +#if !defined (ACE_WIN32) +#include "ace/Atomic_Op.i" +#endif /* !ACE_WIN32 */ +#endif /* __ACE_INLINE__ */ + + +ACE_ALLOC_HOOK_DEFINE(ACE_Atomic_Op_Ex) +ACE_ALLOC_HOOK_DEFINE(ACE_Atomic_Op) + +ACE_RCSID(ace, Atomic_Op, "$Id$") + +// ************************************************* +template ACE_LOCK & +ACE_Atomic_Op_Ex::mutex (void) +{ + // ACE_TRACE ("ACE_Atomic_Op_Ex::mutex"); + return this->mutex_; +} + +template void +ACE_Atomic_Op_Ex::dump (void) const +{ + // ACE_TRACE ("ACE_Atomic_Op_Ex::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->mutex_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template +ACE_Atomic_Op_Ex::ACE_Atomic_Op_Ex + (ACE_LOCK &mtx) + : mutex_ (mtx), + value_ (0) +{ + // ACE_TRACE ("ACE_Atomic_Op_Ex::ACE_Atomic_Op_Ex"); +} + +template +ACE_Atomic_Op_Ex::ACE_Atomic_Op_Ex + (ACE_LOCK &mtx, const TYPE &c) + : mutex_ (mtx), + value_ (c) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::ACE_Atomic_Op_Ex"); +} + +// **************************************************************** + +template +ACE_Atomic_Op::ACE_Atomic_Op (void) + : ACE_Atomic_Op_Ex < ACE_LOCK,TYPE > (this->own_mutex_) +{ + // ACE_TRACE ("ACE_Atomic_Op::ACE_Atomic_Op"); +} + +template +ACE_Atomic_Op::ACE_Atomic_Op (const TYPE &c) + : ACE_Atomic_Op_Ex < ACE_LOCK,TYPE > (this->own_mutex_, c) +{ + // ACE_TRACE ("ACE_Atomic_Op::ACE_Atomic_Op"); +} + +template ACE_INLINE +ACE_Atomic_Op::ACE_Atomic_Op + (const ACE_Atomic_Op &rhs) + : ACE_Atomic_Op_Ex < ACE_LOCK,TYPE > + ( this->own_mutex_, rhs.value() ) +{ +// ACE_TRACE ("ACE_Atomic_Op::ACE_Atomic_Op"); +} + +#endif /*ACE_ATOMIC_OP */ diff --git a/ace/Threads/Atomic_Op.h b/ace/Threads/Atomic_Op.h new file mode 100644 index 00000000000..42e5f15ba08 --- /dev/null +++ b/ace/Threads/Atomic_Op.h @@ -0,0 +1,191 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Atomic_Op.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_ATOMIC_OP_H +#define ACE_ATOMIC_OP_H +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Synch.h" + + +/** + * @class ACE_Atomic_Op_Ex + * + * @brief Transparently parameterizes synchronization into basic + * arithmetic operations. + * + * This class is described in an article in the July/August 1994 + * issue of the C++ Report magazine. It implements a + * templatized version of the Decorator pattern from the GoF book. + */ +template +class ACE_Atomic_Op_Ex +{ +public: + // = Initialization methods. + + /// Initialize to 0. + ACE_Atomic_Op_Ex (ACE_LOCK &mtx); + + /// Initialize to c. + ACE_Atomic_Op_Ex (ACE_LOCK &mtx, const TYPE &c); + + // = Accessors. + + /// Atomically pre-increment . + TYPE operator++ (void); + + /// Atomically post-increment . + TYPE operator++ (int); + + /// Atomically increment by i. + TYPE operator+= (const TYPE &i); + + /// Atomically pre-decrement . + TYPE operator-- (void); + + /// Atomically post-decrement . + TYPE operator-- (int); + + /// Atomically decrement by i. + TYPE operator-= (const TYPE &i); + + /// Atomically compare with i. + int operator== (const TYPE &i) const; + + /// Atomically compare with i. + int operator!= (const TYPE &i) const; + + /// Atomically check if greater than or equal to i. + int operator>= (const TYPE &i) const; + + /// Atomically check if greater than i. + int operator> (const TYPE &rhs) const; + + /// Atomically check if less than or equal to i. + int operator<= (const TYPE &rhs) const; + + /// Atomically check if less than i. + int operator< (const TYPE &rhs) const; + + /// Atomically assign i to . + void operator= (const TYPE &i); + + /// Atomically assign to . + void operator= (const ACE_Atomic_Op_Ex &rhs); + + /// Explicitly return . + TYPE value (void) const; + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + + /// Manage copying... + ACE_Atomic_Op_Ex (const ACE_Atomic_Op_Ex &); + + /** + * Returns a reference to the underlying . This makes it + * possible to acquire the lock explicitly, which can be useful in + * some cases if you instantiate the with an + * or . NOTE: the right + * name would be lock_, but HP/C++ will choke on that! + */ + ACE_LOCK &mutex (void); + + /** + * Explicitly return (by reference). This gives the user + * full, unrestricted access to the underlying value. This method + * will usually be used in conjunction with explicit access to the + * lock. Use with care ;-) + */ + TYPE &value_i (void); + +private: + /// Type of synchronization mechanism. + ACE_LOCK &mutex_; + + /// Current object decorated by the atomic op. + TYPE value_; +}; + +template +class ACE_Atomic_Op : public ACE_Atomic_Op_Ex +{ +public: + /// Initialize to 0. + ACE_Atomic_Op (void); + + /// Initialize to c. + ACE_Atomic_Op (const TYPE &c); + + /// Manage copying... + ACE_Atomic_Op (const ACE_Atomic_Op &); + + /// Atomically assign i to . + void operator= (const TYPE &i); + + /// Atomically assign to . + void operator= (const ACE_Atomic_Op_Ex &rhs); + +private: + /// Type of synchronization mechanism. + ACE_LOCK own_mutex_; +}; + + +#if defined (__ACE_INLINE__) +// On non-Win32 platforms, this code will be inlined +#if !defined (ACE_WIN32) +#include "ace/Atomic_Op.i" +#endif /* !ACE_WIN32 */ +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) + +#include "Atomic_Op.cpp" +// On Win32 platforms, this code will be included as template source +// code and will not be inlined. Therefore, we first turn off +// ACE_INLINE, set it to be nothing, include the code, and then turn +// ACE_INLINE back to its original setting. All this nonsense is +// necessary, since the generic template code that needs to be +// specialized cannot be inlined, else the compiler will ignore the +// specialization code. Also, the specialization code *must* be +// inlined or the compiler will ignore the specializations. +#if defined (ACE_WIN32) +#undef ACE_INLINE +#define ACE_INLINE +#include "ace/Atomic_Op.i" +#undef ACE_INLINE +#if defined (__ACE_INLINE__) +#define ACE_INLINE inline +#else +#define ACE_INLINE +#endif /* __ACE_INLINE__ */ +#endif /* ACE_WIN32 */ +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Atomic_Op.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /*ACE_ATOMIC_OP_H*/ diff --git a/ace/Threads/Atomic_Op.i b/ace/Threads/Atomic_Op.i new file mode 100644 index 00000000000..33ee42fa79f --- /dev/null +++ b/ace/Threads/Atomic_Op.i @@ -0,0 +1,219 @@ +// -*- C++ -*- +// $Id$ + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::operator++ (void) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator++"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, this->value_); + return ++this->value_; +} + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::operator+= (const TYPE &i) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator+="); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, this->value_); + return this->value_ += i; +} + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::operator-- (void) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator--"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, this->value_); + return --this->value_; +} + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::operator-= (const TYPE &i) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator-="); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, this->value_); + return this->value_ -= i; +} + +template ACE_INLINE +ACE_Atomic_Op_Ex::ACE_Atomic_Op_Ex (const ACE_Atomic_Op_Ex &rhs) + : mutex_ (rhs.mutex_) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::ACE_Atomic_Op_Ex"); + *this = rhs; // Invoke the assignment operator. +} + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::operator++ (int) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator++"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, this->value_); + return this->value_++; +} + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::operator-- (int) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator--"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, this->value_); + return this->value_--; +} + +template ACE_INLINE int +ACE_Atomic_Op_Ex::operator== (const TYPE &i) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator=="); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_, -1); + return this->value_ == i; +} + +template ACE_INLINE int +ACE_Atomic_Op_Ex::operator!= (const TYPE &i) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator!="); + return !(*this == i); +} + +template ACE_INLINE int +ACE_Atomic_Op_Ex::operator>= (const TYPE &i) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator>="); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_, -1); + return this->value_ >= i; +} + +template ACE_INLINE int +ACE_Atomic_Op_Ex::operator> (const TYPE &rhs) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator>"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_, -1); + return this->value_ > rhs; +} + +template ACE_INLINE int +ACE_Atomic_Op_Ex::operator<= (const TYPE &rhs) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator<="); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_, -1); + return this->value_ <= rhs; +} + +template ACE_INLINE int +ACE_Atomic_Op_Ex::operator< (const TYPE &rhs) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator<"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_, -1); + return this->value_ < rhs; +} + +template ACE_INLINE void +ACE_Atomic_Op_Ex::operator= (const ACE_Atomic_Op_Ex &rhs) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator="); + if (&rhs == this) + return; // Avoid deadlock... + ACE_GUARD (ACE_LOCK, ace_mon, this->mutex_); + // This will call ACE_Atomic_Op_Ex::TYPE(), which will ensure the value + // of is acquired atomically. + + this->value_ = rhs.value (); +} + +template ACE_INLINE TYPE +ACE_Atomic_Op_Ex::value (void) const +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::value"); + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_, this->value_); + return this->value_; +} + +template ACE_INLINE TYPE & +ACE_Atomic_Op_Ex::value_i (void) +{ + // Explicitly return (by reference). This gives the user + // full, unrestricted access to the underlying value. This method + // will usually be used in conjunction with explicit access to the + // lock. Use with care ;-) + return this->value_; +} + +template ACE_INLINE void +ACE_Atomic_Op_Ex::operator= (const TYPE &i) +{ +// ACE_TRACE ("ACE_Atomic_Op_Ex::operator="); + ACE_GUARD (ACE_LOCK, ace_mon, (ACE_LOCK &) this->mutex_); + this->value_ = i; +} + +// +// ACE_Atomic_Op inline functions +// + +template ACE_INLINE void +ACE_Atomic_Op::operator= (const TYPE &i) +{ + ACE_Atomic_Op_Ex ::operator= (i); +} + +template ACE_INLINE void +ACE_Atomic_Op::operator= (const ACE_Atomic_Op_Ex &rhs) +{ + ACE_Atomic_Op_Ex ::operator= (rhs); +} + +// These specializations have been added to ACE_Atomic_Op_Ex to make the +// implementation faster on Win32 that has OS support for doing this +// quickly through methods like InterlockedIncrement and +// InterlockedDecrement + +#if defined (ACE_WIN32) + +// FUZZ: disable check_for_inline + +ACE_TEMPLATE_SPECIALIZATION +inline long +ACE_Atomic_Op_Ex::operator++ (void) +{ + return ::InterlockedIncrement (&this->value_); +} + +ACE_TEMPLATE_SPECIALIZATION +inline long +ACE_Atomic_Op_Ex::operator-- (void) +{ + return ::InterlockedDecrement (&this->value_); +} + +ACE_TEMPLATE_SPECIALIZATION +inline void +ACE_Atomic_Op_Ex::operator= (const long &i) +{ + ::InterlockedExchange (&this->value_, + i); +} + +ACE_TEMPLATE_SPECIALIZATION +inline void +ACE_Atomic_Op_Ex::operator= (const ACE_Atomic_Op_Ex &rhs) +{ + ::InterlockedExchange (&this->value_, + rhs.value ()); +} + +#if defined (ACE_HAS_INTERLOCKED_EXCHANGEADD) + +ACE_TEMPLATE_SPECIALIZATION +inline long +ACE_Atomic_Op_Ex::operator+= (const long &i) +{ + return ::InterlockedExchangeAdd (&this->value_, i); +} + +ACE_TEMPLATE_SPECIALIZATION +inline long +ACE_Atomic_Op_Ex::operator-= (const long &i) +{ + return ::InterlockedExchangeAdd (&this->value_, -i); +} + +#endif /* ACE_HAS_INTERLOCKED_EXCHANGEADD */ + +#endif /* ACE_WIN32 */ diff --git a/ace/Threads/File_Lock.cpp b/ace/Threads/File_Lock.cpp new file mode 100644 index 00000000000..7f9c3048aa7 --- /dev/null +++ b/ace/Threads/File_Lock.cpp @@ -0,0 +1,78 @@ +// $Id$ + +#include "ace/File_Lock.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/File_Lock.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, File_Lock, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_File_Lock) + +void +ACE_File_Lock::dump (void) const +{ +// ACE_TRACE ("ACE_File_Lock::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->lock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_File_Lock::ACE_File_Lock (ACE_HANDLE h, + int unlink_in_destructor) + : removed_ (0), + unlink_in_destructor_ (unlink_in_destructor) +{ +// ACE_TRACE ("ACE_File_Lock::ACE_File_Lock"); + if (ACE_OS::flock_init (&this->lock_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_File_Lock::ACE_File_Lock"))); + this->set_handle (h); +} + +ACE_File_Lock::ACE_File_Lock (const ACE_TCHAR *name, + int flags, + mode_t perms, + int unlink_in_destructor) + : unlink_in_destructor_ (unlink_in_destructor) +{ +// ACE_TRACE ("ACE_File_Lock::ACE_File_Lock"); + + if (this->open (name, flags, perms) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p %s\n"), + ACE_LIB_TEXT ("ACE_File_Lock::ACE_File_Lock"), + name)); +} + +int +ACE_File_Lock::open (const ACE_TCHAR *name, + int flags, + mode_t perms) +{ +// ACE_TRACE ("ACE_File_Lock::open"); + this->removed_ = 0; + return ACE_OS::flock_init (&this->lock_, flags, name, perms); +} + +ACE_File_Lock::~ACE_File_Lock (void) +{ +// ACE_TRACE ("ACE_File_Lock::~ACE_File_Lock"); + this->remove (this->unlink_in_destructor_); +} + +// These are instantiated both with and without ACE_HAS_THREADS. + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) + +// template class ACE_Guard; + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) + +// #pragma instantiate ACE_Guard + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Threads/File_Lock.h b/ace/Threads/File_Lock.h new file mode 100644 index 00000000000..afc549a0ff0 --- /dev/null +++ b/ace/Threads/File_Lock.h @@ -0,0 +1,164 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file File_Lock.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_FILE_LOCK_H +#define ACE_FILE_LOCK_H +#include "ace/pre.h" + +#include "ace/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_File_Lock + * + * @brief A wrapper around the UNIX file locking mechanism. + * + * Allows us to "adapt" the UNIX file locking mechanisms to work + * with all of our Guard stuff... + */ +class ACE_Export ACE_File_Lock +{ +public: + /** + * Set the of the File_Lock to . Note that this + * constructor assumes ownership of the and will close it + * down in . If you want the to stay open when + * is called make sure to call on the . + * If you don't want the file unlinked in the destructor pass a + * zero value for . + */ + ACE_File_Lock (ACE_HANDLE handle = ACE_INVALID_HANDLE, + int unlink_in_destructor = 1); + + /// Open the with and and set the result + /// to . If you don't want the file unlinked in the + /// destructor pass a zero value for . + ACE_File_Lock (const ACE_TCHAR *filename, + int flags, + mode_t mode = 0, + int unlink_in_destructor = 1); + + /// Open the with and and set the result to + /// . + int open (const ACE_TCHAR *filename, + int flags, + mode_t mode = 0); + + /// Remove a File lock by releasing it and closing down the . + ~ACE_File_Lock (void); + + /// Remove a File lock by releasing it and closing down the + /// . If is non-0 then we unlink the file. + int remove (int unlink_file = 1); + + /** + * Note, for interface uniformity with other synchronization + * wrappers we include the method. This is implemented as + * a write-lock to be on the safe-side... + */ + int acquire (short whence = 0, off_t start = 0, off_t len = 1); + + /** + * Note, for interface uniformity with other synchronization + * wrappers we include the method. This is implemented + * as a write-lock to be on the safe-side... Returns -1 on failure. + * If we "failed" because someone else already had the lock, + * is set to . + */ + int tryacquire (short whence = 0, off_t start = 0, off_t len = 1); + + /// Unlock a readers/writer lock. + int release (short whence = 0, off_t start = 0, off_t len = 1); + + /// Acquire a write lock, but block if any readers or a + /// writer hold the lock. + int acquire_write (short whence = 0, off_t start = 0, off_t len = 1); + + /** + * Conditionally acquire a write lock (i.e., won't block). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire_write (short whence = 0, off_t start = 0, off_t len = 1); + + /** + * Conditionally upgrade to a write lock (i.e., won't block). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire_write_upgrade (short whence = 0, + off_t start = 0, + off_t len = 1); + + /** + * Acquire a read lock, but block if a writer hold the lock. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int acquire_read (short whence = 0, off_t start = 0, off_t len = 1); + + /** + * Conditionally acquire a read lock (i.e., won't block). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire_read (short whence = 0, off_t start = 0, off_t len = 1); + + /// Get underlying for the file. + ACE_HANDLE get_handle (void) const; + + /** + * Set underlying . Note that this method assumes + * ownership of the and will close it down in . If + * you want the to stay open when is called make + * sure to call on the before closing it. You are + * responsible for the closing the existing before + * overwriting it. + */ + void set_handle (ACE_HANDLE); + + /// Dump state of the object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Locking structure for OS record locks. + ACE_OS::ace_flock_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + + /// Keeps track of whether to unlink the underlying file in the + /// destructor. + int unlink_in_destructor_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_File_Lock &); + ACE_File_Lock (const ACE_File_Lock &); +}; + +#if defined (__ACE_INLINE__) +#include "ace/File_Lock.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_FILE_LOCK_H */ diff --git a/ace/Threads/File_Lock.inl b/ace/Threads/File_Lock.inl new file mode 100644 index 00000000000..a0dc79dcc7b --- /dev/null +++ b/ace/Threads/File_Lock.inl @@ -0,0 +1,89 @@ +/* -*- C++ -*- */ +// $Id$ + +ACE_INLINE int +ACE_File_Lock::acquire_read (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::acquire_read"); + return ACE_OS::flock_rdlock (&this->lock_, whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::tryacquire_read (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::tryacquire_read"); + return ACE_OS::flock_tryrdlock (&this->lock_, whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::tryacquire_write (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::tryacquire_write"); + return ACE_OS::flock_trywrlock (&this->lock_, whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::tryacquire_write_upgrade (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::tryacquire_write_upgrade"); + return ACE_OS::flock_trywrlock (&this->lock_, whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::tryacquire (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::tryacquire"); + return this->tryacquire_write (whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::acquire_write (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::acquire_write"); + return ACE_OS::flock_wrlock (&this->lock_, whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::acquire (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::acquire"); + return this->acquire_write (whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::release (short whence, off_t start, off_t len) +{ +// ACE_TRACE ("ACE_File_Lock::release"); + return ACE_OS::flock_unlock (&this->lock_, whence, start, len); +} + +ACE_INLINE int +ACE_File_Lock::remove (int unlink_file) +{ +// ACE_TRACE ("ACE_File_Lock::remove"); + + int result = 0; + + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::flock_destroy (&this->lock_, + unlink_file); + } + return result; +} + +ACE_INLINE ACE_HANDLE +ACE_File_Lock::get_handle (void) const +{ +// ACE_TRACE ("ACE_File_Lock::get_handle"); + return this->lock_.handle_; +} + +ACE_INLINE void +ACE_File_Lock::set_handle (ACE_HANDLE h) +{ +// ACE_TRACE ("ACE_File_Lock::set_handle"); + this->lock_.handle_ = h; + this->removed_ = 0; +} diff --git a/ace/Threads/Makefile b/ace/Threads/Makefile new file mode 100644 index 00000000000..d75c4c95f54 --- /dev/null +++ b/ace/Threads/Makefile @@ -0,0 +1,65 @@ +# $Id$ + +#---------------------------------------------------------------------------- +# Makefile for the libACE_Threads +#---------------------------------------------------------------------------- + +MAKEFILE = Makefile +LIBOS = libACE_Threads +LIB = $(LIBOS).a +SHLIB = $(LIBOS).$(SOEXT) + + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU + +#### +#### ACE_COMPONENTS support. +#### +FILES += \ + Activation_Queue\ + Atomic_Op\ + File_Lock\ + Process\ + Process_Manager\ + Process_Mutex\ + Process_Semaphore\ + RW_Process_Mutex\ + Synch\ + Synch_Options\ + Synch_T\ + Thread_Adapter\ + Thread_Control\ + Thread\ + Thread_Exit\ + Thread_Manager\ + Token + +LSRC = $(addsuffix .cpp,$(FILES)) + +include $(ACE_ROOT)/include/makeinclude/macros.GNU +include $(ACE_ROOT)/include/makeinclude/rules.common.GNU +include $(ACE_ROOT)/include/makeinclude/rules.nested.GNU +include $(ACE_ROOT)/include/makeinclude/rules.lib.GNU +include $(ACE_ROOT)/include/makeinclude/rules.local.GNU + +INSTALL = + +clean: + $(RM) -f $(LIBOS).a $(LIBOS).so + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + + + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + diff --git a/ace/Threads/Process.cpp b/ace/Threads/Process.cpp new file mode 100644 index 00000000000..932e355072a --- /dev/null +++ b/ace/Threads/Process.cpp @@ -0,0 +1,893 @@ +// $Id$ + +#include "ace/OS.h" +#include "ace/Process.h" +#include "ace/ARGV.h" +#include "ace/Signal.h" +#include "ace/SString.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Process.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (ace, Process, "$Id$") + +ACE_Process::ACE_Process (void) + : +#if !defined (ACE_WIN32) + child_id_ (ACE_INVALID_PID), +#endif /* !defined (ACE_WIN32) */ + exit_code_ (0) +{ +#if defined (ACE_WIN32) + ACE_OS::memset ((void *) &this->process_info_, + 0, + sizeof this->process_info_); +#endif /* ACE_WIN32 */ +} + +ACE_Process::~ACE_Process (void) +{ +#if defined (ACE_WIN32) + // Free resources allocated in kernel. + ACE_OS::close (this->process_info_.hThread); + ACE_OS::close (this->process_info_.hProcess); +#endif /* ACE_WIN32 */ + // If any handles were duplicated for the child process and + // still not closed, get them now. + this->close_dup_handles (); +} + +int +ACE_Process::prepare (ACE_Process_Options &) +{ + return 0; +} + +pid_t +ACE_Process::spawn (ACE_Process_Options &options) +{ + if (prepare (options) < 0) + return ACE_INVALID_PID; + + // Stash the passed/duped handle sets away in this object for later + // closing if needed or requested. At the same time, figure out which + // ones to include in command line options if that's needed below. + ACE_Handle_Set *set_p = 0; + if (options.dup_handles (this->dup_handles_)) + set_p = &this->dup_handles_; + else if (options.passed_handles (this->handles_passed_)) + set_p = &this->handles_passed_; + + // If we are going to end up running a new program (i.e. Win32, or + // NO_EXEC option is set) then get any handles passed in the options, + // and tack them onto the command line with +H options, + // unless the command line runs out of space. + // Note that we're using the knowledge that all the options, argvs, etc. + // passed to the options are all sitting in the command_line_buf. Any + // call to get the argv then splits them out. So, regardless of the + // platform, tack them all onto the command line buf and take it + // from there. + if (set_p && !ACE_BIT_ENABLED (options.creation_flags (), + ACE_Process_Options::NO_EXEC)) + { + int maxlen = 0; + ACE_TCHAR *cmd_line_buf = options.command_line_buf (&maxlen); + size_t max_len = ACE_static_cast (size_t, maxlen); + size_t curr_len = ACE_OS::strlen (cmd_line_buf); + ACE_Handle_Set_Iterator h_iter (*set_p); + // Because the length of the to-be-formatted +H option is not + // known, and we don't have a snprintf, guess at the space + // needed (20 chars), and use that as a limit. + for (ACE_HANDLE h = h_iter (); + h != ACE_INVALID_HANDLE && curr_len + 20 < max_len; + h = h_iter ()) + { + curr_len += ACE_OS::sprintf (&cmd_line_buf[curr_len], + ACE_LIB_TEXT (" +H %d"), + h); + } + } + +#if defined (ACE_WIN32) + BOOL fork_result = + ACE_TEXT_CreateProcess (0, + options.command_line_buf (), + options.get_process_attributes (), + options.get_thread_attributes (), + options.handle_inheritence (), + options.creation_flags (), + options.env_buf (), // environment variables + options.working_directory (), + options.startup_info (), + &this->process_info_); + + if (fork_result) + { + parent (this->getpid ()); + return this->getpid (); + } + return ACE_INVALID_PID; + +#elif defined (CHORUS) + // This only works if we exec. Chorus does not really support + // forking. + if (ACE_BIT_ENABLED (options.creation_flags (), + ACE_Process_Options::NO_EXEC)) + ACE_NOTSUP_RETURN (ACE_INVALID_PID); + + // These are all currently unsupported. + if (options.get_stdin () != ACE_INVALID_HANDLE) + ACE_NOTSUP_RETURN (ACE_INVALID_PID); + if (options.get_stdout () != ACE_INVALID_HANDLE) + ACE_NOTSUP_RETURN (ACE_INVALID_PID); + if (options.get_stderr () != ACE_INVALID_HANDLE) + ACE_NOTSUP_RETURN (ACE_INVALID_PID); + if (options.working_directory () != 0) + ACE_NOTSUP_RETURN (ACE_INVALID_PID); + + if (options.env_argv ()[0] == 0) + // command-line args + this->child_id_ = ACE_OS::execvp (options.process_name (), + options.command_line_argv ()); + else + { + // Add the new environment variables to the environment context + // of the context before doing an . + for (char *const *user_env = options.env_argv (); + *user_env != 0; + user_env++) + if (ACE_OS::putenv (*user_env) != 0) + return ACE_INVALID_PID; + + // Now the forked process has both inherited variables and the + // user's supplied variables. + this->child_id_ = ACE_OS::execvp (options.process_name (), + options.command_line_argv ()); + } + + return this->child_id_; +#else /* ACE_WIN32 */ + // Fork the new process. + this->child_id_ = ACE::fork (options.process_name (), + options.avoid_zombies ()); + + if (this->child_id_ == 0) + { + // If we're the child and the options specified a non-default + // process group, try to set our pgid to it. This allows the + // to wait for processes by their + // process-group. + if (options.getgroup () != ACE_INVALID_PID + && ACE_OS::setpgid (0, + options.getgroup ()) < 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p.\n"), + ACE_LIB_TEXT ("ACE_Process::spawn: setpgid failed."))); + +#if !defined (ACE_LACKS_SETREGID) + if (options.getrgid () != (uid_t) -1 + || options.getegid () != (uid_t) -1) + if (ACE_OS::setregid (options.getrgid (), + options.getegid ()) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p.\n"), + ACE_LIB_TEXT ("ACE_Process::spawn: setregid failed."))); +#endif /* ACE_LACKS_SETREGID */ + +#if !defined (ACE_LACKS_SETREUID) + // Set user and group id's. + if (options.getruid () != (uid_t) -1 + || options.geteuid () != (uid_t) -1) + if (ACE_OS::setreuid (options.getruid (), + options.geteuid ()) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p.\n"), + ACE_LIB_TEXT ("ACE_Process::spawn: setreuid failed."))); +#endif /* ACE_LACKS_SETREUID */ + + this->child (ACE_OS::getppid ()); + } + else if (this->child_id_ != -1) + this->parent (this->child_id_); + + // If we're not supposed to exec, return the process id. + if (ACE_BIT_ENABLED (options.creation_flags (), + ACE_Process_Options::NO_EXEC)) + return this->child_id_; + + switch (this->child_id_) + { + case -1: + // Error. + return ACE_INVALID_PID; + case 0: + // Child process...exec the + { + if (options.get_stdin () != ACE_INVALID_HANDLE + && ACE_OS::dup2 (options.get_stdin (), + ACE_STDIN) == -1) + ACE_OS::exit (errno); + else if (options.get_stdout () != ACE_INVALID_HANDLE + && ACE_OS::dup2 (options.get_stdout (), + ACE_STDOUT) == -1) + ACE_OS::exit (errno); + else if (options.get_stderr () != ACE_INVALID_HANDLE + && ACE_OS::dup2 (options.get_stderr (), + ACE_STDERR) == -1) + ACE_OS::exit (errno); + + // close down unneeded descriptors + ACE_OS::close (options.get_stdin ()); + ACE_OS::close (options.get_stdout ()); + ACE_OS::close (options.get_stderr ()); + + // If we must, set the working directory for the child + // process. + if (options.working_directory () != 0) + ACE_OS::chdir (options.working_directory ()); + // Should check for error here! + + // Child process executes the command. + int result = 0; + + if (options.env_argv ()[0] == 0) + // command-line args + result = ACE_OS::execvp (options.process_name (), + options.command_line_argv ()); + else + { +#if defined (ghs) + // GreenHills 1.8.8 (for VxWorks 5.3.x) can't compile this + // code. Processes aren't supported on VxWorks anyways. + ACE_NOTSUP_RETURN (ACE_INVALID_PID); +#else + // Add the new environment variables to the environment + // context of the context before doing an . + for (char *const *user_env = options.env_argv (); + *user_env != 0; + user_env++) + if (ACE_OS::putenv (*user_env) != 0) + return ACE_INVALID_PID; + + // Now the forked process has both inherited variables and + // the user's supplied variables. + result = ACE_OS::execvp (options.process_name (), + options.command_line_argv ()); +#endif /* ghs */ + } + if (result == -1) + { + // If the execv fails, this child needs to exit. + + // Exit with the errno so that the calling process can + // catch this and figure out what went wrong. + ACE_OS::exit (errno); + } + // ... otherwise, this is never reached. + return 0; + } + default: + // Server process. The fork succeeded. + return this->child_id_; + } +#endif /* ACE_WIN32 */ +} + +void +ACE_Process::parent (pid_t) +{ + // nothing to do +} + +void +ACE_Process::child (pid_t) +{ + // nothing to do +} + +void +ACE_Process::unmanage (void) +{ + // nothing to do +} + +int +ACE_Process::running (void) const +{ +#if defined (ACE_WIN32) + DWORD code; + + BOOL result = ::GetExitCodeProcess (this->gethandle (), + &code); + return result && code == STILL_ACTIVE; +#else + return ACE_OS::kill (this->getpid (), + 0) == 0 + || errno != ESRCH; +#endif /* ACE_WIN32 */ +} + +pid_t +ACE_Process::wait (const ACE_Time_Value &tv, + ACE_exitcode *status) +{ +#if defined (ACE_WIN32) + // Don't try to get the process exit status if wait failed so we can + // keep the original error code intact. + switch (::WaitForSingleObject (process_info_.hProcess, + tv.msec ())) + { + case WAIT_OBJECT_0: + if (status != 0) + // The error status of is nonetheless not + // tested because we don't know how to return the value. + ::GetExitCodeProcess (process_info_.hProcess, + status); + return this->getpid (); + case WAIT_TIMEOUT: + errno = ETIME; + return 0; + default: + ACE_OS::set_errno_to_last_error (); + return -1; + } +#else /* ACE_WIN32 */ + if (tv == ACE_Time_Value::zero) + ACE_OSCALL_RETURN (ACE_OS::waitpid (this->child_id_, + status, + WNOHANG), + int, ACE_INVALID_PID); + + if (tv == ACE_Time_Value::max_time) + return this->wait (status); + + ACE_Time_Value wait_until = ACE_OS::gettimeofday () + tv; + + for (;;) + { + int result = ACE_OS::waitpid (this->getpid (), + status, + WNOHANG); + if (result != 0) + return result; + + ACE_Sig_Set alarm_or_child; + + alarm_or_child.sig_add (SIGALRM); + alarm_or_child.sig_add (SIGCHLD); + + ACE_Time_Value time_left = wait_until - ACE_OS::gettimeofday (); + + // If ACE_OS::ualarm doesn't have sub-second resolution: + time_left += ACE_Time_Value (0, 500000); + time_left.usec (0); + + if (time_left <= ACE_Time_Value::zero) + return 0; // timeout + + ACE_OS::ualarm (time_left); + if (ACE_OS::sigwait (alarm_or_child) == -1) + return ACE_INVALID_PID; + } +#endif /* ACE_WIN32 */ +} + +void +ACE_Process::close_dup_handles (void) +{ + if (this->dup_handles_.num_set () > 0) + { + ACE_Handle_Set_Iterator h_iter (this->dup_handles_); + for (ACE_HANDLE h = h_iter (); + h != ACE_INVALID_HANDLE; + h = h_iter ()) + ACE_OS::closesocket (h); + this->dup_handles_.reset (); + } + return; +} + +void +ACE_Process::close_passed_handles (void) +{ + if (this->handles_passed_.num_set () > 0) + { + ACE_Handle_Set_Iterator h_iter (this->handles_passed_); + for (ACE_HANDLE h = h_iter (); + h != ACE_INVALID_HANDLE; + h = h_iter ()) + ACE_OS::closesocket (h); + this->handles_passed_.reset (); + } + return; +} + + +ACE_Process_Options::ACE_Process_Options (int ie, + int cobl, + int ebl, + int mea) + : +#if !defined (ACE_HAS_WINCE) + inherit_environment_ (ie), +#endif /* ACE_HAS_WINCE */ + creation_flags_ (0), + avoid_zombies_ (0), +#if !defined (ACE_HAS_WINCE) +#if defined (ACE_WIN32) + environment_inherited_ (0), + handle_inheritence_ (TRUE), + process_attributes_ (NULL), + thread_attributes_ (NULL), +#else /* ACE_WIN32 */ + stdin_ (ACE_INVALID_HANDLE), + stdout_ (ACE_INVALID_HANDLE), + stderr_ (ACE_INVALID_HANDLE), + ruid_ ((uid_t) -1), + euid_ ((uid_t) -1), + rgid_ ((uid_t) -1), + egid_ ((uid_t) -1), +#endif /* ACE_WIN32 */ + set_handles_called_ (0), + environment_buf_index_ (0), + environment_argv_index_ (0), + environment_buf_ (0), + environment_buf_len_ (ebl), + max_environment_args_ (mea), + max_environ_argv_index_ (mea - 1), +#endif /* !ACE_HAS_WINCE */ + command_line_argv_calculated_ (0), + command_line_buf_ (0), + command_line_buf_len_ (cobl), + process_group_ (ACE_INVALID_PID) +{ + ACE_NEW (command_line_buf_, + ACE_TCHAR[cobl]); + command_line_buf_[0] = '\0'; + +#if !defined (ACE_HAS_WINCE) + working_directory_[0] = '\0'; + ACE_NEW (environment_buf_, + ACE_TCHAR[ebl]); + ACE_NEW (environment_argv_, + ACE_TCHAR *[mea]); + environment_buf_[0] = '\0'; + environment_argv_[0] = 0; + process_name_[0] = '\0'; +#if defined (ACE_WIN32) + ACE_OS::memset ((void *) &this->startup_info_, + 0, + sizeof this->startup_info_); + this->startup_info_.cb = sizeof this->startup_info_; +#endif /* ACE_WIN32 */ +#endif /* !ACE_HAS_WINCE */ +} + +#if !defined (ACE_HAS_WINCE) +#if defined (ACE_WIN32) +void +ACE_Process_Options::inherit_environment (void) +{ + // Ensure only once execution. + if (environment_inherited_) + return; + environment_inherited_ = 1; + + // Get the existing environment. + ACE_TCHAR *existing_environment = ACE_OS::getenvstrings (); + + int slot = 0; + + while (existing_environment[slot] != '\0') + { + int len = ACE_OS::strlen (existing_environment + slot); + + // Add the string to our env buffer. + if (this->setenv_i (existing_environment + slot, len) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p.\n"), + ACE_LIB_TEXT ("ACE_Process_Options::ACE_Process_Options"))); + break; + } + + // Skip to the next word. + slot += len + 1; + } + + ACE_TEXT_FreeEnvironmentStrings (existing_environment); +} + +#else /* defined ACE_WIN32 */ + +ACE_TCHAR * const * +ACE_Process_Options::env_argv (void) +{ + return environment_argv_; +} + +#endif /* ACE_WIN32 */ + +int +ACE_Process_Options::setenv (ACE_TCHAR *envp[]) +{ + int i = 0; + while (envp[i]) + { + if (this->setenv_i (envp[i], + ACE_OS::strlen (envp[i])) == -1) + return -1; + i++; + } + +#if defined (ACE_WIN32) + if (inherit_environment_) + this->inherit_environment (); +#endif /* ACE_WIN32 */ + + return 0; +} + +int +ACE_Process_Options::setenv (const ACE_TCHAR *format, ...) +{ + ACE_TCHAR stack_buf[DEFAULT_COMMAND_LINE_BUF_LEN]; + + // Start varargs. + va_list argp; + va_start (argp, format); + + // Add the rest of the varargs. + ACE_OS::vsprintf (stack_buf, + format, + argp); + // End varargs. + va_end (argp); + + // Append the string to are environment buffer. + if (this->setenv_i (stack_buf, + ACE_OS::strlen (stack_buf)) == -1) + return -1; + +#if defined (ACE_WIN32) + if (inherit_environment_) + this->inherit_environment (); +#endif /* ACE_WIN32 */ + + return 0; +} + +int +ACE_Process_Options::setenv (const ACE_TCHAR *variable_name, + const ACE_TCHAR *format, ...) +{ + ACE_TCHAR newformat[DEFAULT_COMMAND_LINE_BUF_LEN]; + + // Add in the variable name. + ACE_OS::sprintf (newformat, + ACE_LIB_TEXT ("%s=%s"), + variable_name, + format); + + ACE_TCHAR stack_buf[DEFAULT_COMMAND_LINE_BUF_LEN]; + + // Start varargs. + va_list argp; + va_start (argp, format); + + // Add the rest of the varargs. + ACE_OS::vsprintf (stack_buf, newformat, argp); + + // End varargs. + va_end (argp); + + // Append the string to our environment buffer. + if (this->setenv_i (stack_buf, + ACE_OS::strlen (stack_buf)) == -1) + return -1; + +#if defined (ACE_WIN32) + if (inherit_environment_) + this->inherit_environment (); +#endif /* ACE_WIN32 */ + + return 0; +} + +int +ACE_Process_Options::setenv_i (ACE_TCHAR *assignment, + int len) +{ + // Add one for the null char. + len++; + + // If environment larger than allocated buffer return. Also check to + // make sure we have enough room. + if (environment_argv_index_ == max_environ_argv_index_ + || (len + environment_buf_index_) >= environment_buf_len_) + return -1; + + // Copy the new environment string. + ACE_OS::memcpy (environment_buf_ + environment_buf_index_, + assignment, + len * sizeof (ACE_TCHAR)); + + // Update the argv array. + environment_argv_[environment_argv_index_++] = + environment_buf_ + environment_buf_index_; + environment_argv_[environment_argv_index_] = 0; + + // Update our index. + environment_buf_index_ += len; + + // Make sure the buffer is null-terminated. + environment_buf_[environment_buf_index_] = '\0'; + return 0; +} + +int +ACE_Process_Options::set_handles (ACE_HANDLE std_in, + ACE_HANDLE std_out, + ACE_HANDLE std_err) +{ + this->set_handles_called_ = 1; +#if defined (ACE_WIN32) + + // Tell the new process to use our std handles. + this->startup_info_.dwFlags = STARTF_USESTDHANDLES; + + if (std_in == ACE_INVALID_HANDLE) + std_in = ACE_STDIN; + if (std_out == ACE_INVALID_HANDLE) + std_out = ACE_STDOUT; + if (std_err == ACE_INVALID_HANDLE) + std_err = ACE_STDERR; + + if (!::DuplicateHandle (::GetCurrentProcess (), + std_in, + ::GetCurrentProcess (), + &this->startup_info_.hStdInput, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + return -1; + + if (!::DuplicateHandle (::GetCurrentProcess (), + std_out, + ::GetCurrentProcess (), + &this->startup_info_.hStdOutput, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + return -1; + + if (!::DuplicateHandle (::GetCurrentProcess (), + std_err, + ::GetCurrentProcess (), + &this->startup_info_.hStdError, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + return -1; +#else /* ACE_WIN32 */ + this->stdin_ = ACE_OS::dup (std_in); + this->stdout_ = ACE_OS::dup (std_out); + this->stderr_ = ACE_OS::dup (std_err); +#endif /* ACE_WIN32 */ + + return 0; // Success. +} +#endif /* !ACE_HAS_WINCE */ + +ACE_Process_Options::~ACE_Process_Options (void) +{ +#if !defined (ACE_HAS_WINCE) + if (set_handles_called_) + { +#if defined (ACE_WIN32) + ACE_OS::close (startup_info_.hStdInput); + ACE_OS::close (startup_info_.hStdOutput); + ACE_OS::close (startup_info_.hStdError); +#else /* ACE_WIN32 */ + ACE_OS::close (stdin_); + ACE_OS::close (stdout_); + ACE_OS::close (stderr_); +#endif /* ACE_WIN32 */ + set_handles_called_ = 0; + } + delete [] environment_buf_; + delete [] environment_argv_; +#endif /* !ACE_HAS_WINCE */ + delete [] command_line_buf_; +} + +int +ACE_Process_Options::command_line (const ACE_TCHAR *const argv[]) +{ + // @@ Factor out the code between this + int i = 0; + + if (argv[i]) + { + ACE_OS::strcat (command_line_buf_, argv[i]); + while (argv[++i]) + { + ACE_OS::strcat (command_line_buf_, + ACE_LIB_TEXT (" ")); + ACE_OS::strcat (command_line_buf_, + argv[i]); + } + } + + return 0; // Success. +} + +int +ACE_Process_Options::command_line (const ACE_TCHAR *format, ...) +{ + // Store all ... args in argp. + va_list argp; + va_start (argp, format); + + // sprintf the format and args into command_line_buf__. + ACE_OS::vsprintf (command_line_buf_, + format, + argp); + + // Useless macro. + va_end (argp); + + return 0; +} + +#if defined (ACE_HAS_WCHAR) && !defined (ACE_HAS_WINCE) +/** + * @note Not available on Windows CE because it doesn't have a char version of + * vsprintf. + */ +int +ACE_Process_Options::command_line (const ACE_ANTI_TCHAR *format, ...) +{ + ACE_ANTI_TCHAR *anti_clb; + ACE_NEW_RETURN (anti_clb, + ACE_ANTI_TCHAR[this->command_line_buf_len_], + -1); + + // Store all ... args in argp. + va_list argp; + va_start (argp, format); + + // sprintf the format and args into command_line_buf_. + ACE_OS::vsprintf (anti_clb, + format, + argp); + + // Useless macro. + va_end (argp); + + ACE_OS::strcpy (this->command_line_buf_, + ACE_TEXT_ANTI_TO_TCHAR (anti_clb)); + + delete [] anti_clb; + + return 0; +} +#endif /* ACE_HAS_WCHAR && !ACE_HAS_WINCE */ + +ACE_TCHAR * +ACE_Process_Options::env_buf (void) +{ +#if !defined (ACE_HAS_WINCE) + if (environment_buf_[0] == '\0') + return 0; + else + return environment_buf_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_TCHAR * const * +ACE_Process_Options::command_line_argv (void) +{ + if (command_line_argv_calculated_ == 0) + { + command_line_argv_calculated_ = 1; + + // This tokenizer will replace all spaces with end-of-string + // characters and will preserve text between "" and '' pairs. + ACE_Tokenizer parser (command_line_buf_); + parser.delimiter_replace (' ', '\0'); + parser.preserve_designators ('\"', '\"'); // " + parser.preserve_designators ('\'', '\''); + + int x = 0; + do + command_line_argv_[x] = parser.next (); + while (command_line_argv_[x] != 0 + // substract one for the ending zero. + && ++x < MAX_COMMAND_LINE_OPTIONS - 1); + + command_line_argv_[x] = 0; + } + + return command_line_argv_; +} + + +// Cause the specified handle to be passed to a child process +// when it's spawned. +int +ACE_Process_Options::pass_handle (ACE_HANDLE h) +{ +# if defined (ACE_WIN32) +# if defined (ACE_HAS_WINCE) + ACE_NOTSUP_RETURN (-1); +# else + + // This is oriented towards socket handles... may need some adjustment + // for non-sockets. + // This is all based on an MSDN article: + // http://support.microsoft.com/support/kb/articles/Q150/5/23.asp + // If on Win95/98, the handle needs to be duplicated for the to-be-spawned + // process. On WinNT, they get inherited by the child process automatically. + // If the handle is duplicated, remember the duplicate so it can be + // closed later. Can't be closed now, or the child won't get it. + OSVERSIONINFO osvi; + ZeroMemory (&osvi, sizeof (osvi)); + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + // If this is Win95/98 or we can't tell, duplicate the handle. + if (!GetVersionEx (&osvi) || osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + HANDLE dup_handle; + if (!DuplicateHandle (GetCurrentProcess (), + ACE_static_cast (HANDLE, h), + GetCurrentProcess (), + &dup_handle, + 0, + TRUE, // Inheritable + DUPLICATE_SAME_ACCESS)) + return -1; + dup_handles_.set_bit (ACE_static_cast (ACE_HANDLE, dup_handle)); + } +# endif /* ACE_HAS_WINCE */ +#endif /* ACE_WIN32 */ + + this->handles_passed_.set_bit (h); + + return 0; +} + +// Get a copy of the handles the ACE_Process_Options duplicated +// for the spawned process. +int +ACE_Process_Options::dup_handles (ACE_Handle_Set &set) const +{ + if (this->dup_handles_.num_set () == 0) + return 0; + set.reset (); + set = this->dup_handles_; + return 1; +} + +// Get a copy of the handles passed to the spawned process. This +// will be the set of handles previously passed to @arg pass_handle(). +int +ACE_Process_Options::passed_handles (ACE_Handle_Set &set) const +{ + if (this->handles_passed_.num_set () == 0) + return 0; + set.reset (); + set = this->handles_passed_; + return 1; +} + +ACE_Managed_Process::ACE_Managed_Process (void) +{ +} + +ACE_Managed_Process::~ACE_Managed_Process (void) +{ +} diff --git a/ace/Threads/Process.h b/ace/Threads/Process.h new file mode 100644 index 00000000000..796cba44b4b --- /dev/null +++ b/ace/Threads/Process.h @@ -0,0 +1,557 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Process.h + * + * $Id$ + * + * @author Tim Harrison + */ +//============================================================================= + +#ifndef ACE_PROCESS_H +#define ACE_PROCESS_H +#include "ace/pre.h" + +#include "ace/OS.h" +#include "ace/Handle_Set.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_Process_Options + * + * @brief Process Options + * + * This class controls the options passed to (or + * and ). + * Notice that on Windows CE, creating a process merely means + * instantiating a new process. You can't set the handles (since + * there's no stdin, stdout and stderr,) specify process/thread + * options, set environment,... So, basically, this class only + * set the command line and nothing else. + * Notice that on UNIX platforms, if the is used, the + * is using the system call. It means that the + * should include a full path to the program file + * ( does not search the PATH). If is not used + * then, the is using the which searches for the + * program file in the PATH variable. + */ +class ACE_Export ACE_Process_Options +{ +public: + enum + { + DEFAULT_COMMAND_LINE_BUF_LEN = 1024, + // UNIX process creation flags. +#if defined (ACE_WIN32) + NO_EXEC = 0 +#else + NO_EXEC = 1 +#endif /* ACE_WIN32 */ + }; + +protected: + // = Default settings not part of public Interface. + // + // @@todo These sizes should be taken from the appropriate + // POSIX/system header files and/or defined dynamically. + enum + { + MAX_COMMAND_LINE_OPTIONS = 128, + ENVIRONMENT_BUFFER = 16 * 1024, // 16K + MAX_ENVIRONMENT_ARGS = 512 // + }; + +public: + /** + * If == 1, the new process will inherit the + * environment of the current process. is the + * max strlen for command-line arguments. + */ + ACE_Process_Options (int inherit_environment = 1, + int command_line_buf_len = DEFAULT_COMMAND_LINE_BUF_LEN, + int env_buf_len = ENVIRONMENT_BUFFER, + int max_env_args = MAX_ENVIRONMENT_ARGS); + + /// Destructor. + ~ACE_Process_Options (void); + + // = Methods to set process creation options portably. + + /** + * Set the standard handles of the new process to the respective + * handles. If you want to affect a subset of the handles, make + * sure to set the others to ACE_INVALID_HANDLE. Returns 0 on + * success, -1 on failure. + */ + int set_handles (ACE_HANDLE std_in, + ACE_HANDLE std_out = ACE_INVALID_HANDLE, + ACE_HANDLE std_err = ACE_INVALID_HANDLE); + + /// must be of the form "VARIABLE=VALUE". There can not be + /// any spaces between VARIABLE and the equal sign. + int setenv (const ACE_TCHAR *format, + ...); + + /** + * Set a single environment variable, . Since + * different platforms separate each environment variable + * differently, you must call this method once for each variable. + * can be any printf format string. So options->setenv + * ("FOO","one + two = %s", "three") will result in "FOO=one + two = + * three". + */ + int setenv (const ACE_TCHAR *variable_name, + const ACE_TCHAR *format, + ...); + + /// Same as above with argv format. must be null terminated. + int setenv (ACE_TCHAR *envp[]); + + /// Set the working directory for the process. strlen of must + /// be <= MAXPATHLEN. + void working_directory (const char *wd); + +#if defined (ACE_HAS_WCHAR) + /// wchar_t version of working_directory + void working_directory (const wchar_t *wd); +#endif /* ACE_HAS_WCHAR */ + + /** + * Set the command-line arguments. can use any printf + * formats. The first token in should be the path to the + * application. This can either be a full path, relative path, or + * just an executable name. If an executable name is used, we rely + * on the platform's support for searching paths. Since we need a + * path to run a process, this method *must* be called! Returns 0 + * on success, -1 on failure. + */ + int command_line (const ACE_TCHAR *format, ...); + +#if defined (ACE_HAS_WCHAR) && !defined (ACE_HAS_WINCE) + /// Anti-TChar version of command_line () + int command_line (const ACE_ANTI_TCHAR *format, ...); +#endif /* ACE_HAS_WCHAR && !ACE_HAS_WINCE */ + + /// Same as above in argv format. must be null terminated. + int command_line (const ACE_TCHAR * const argv[]); + + // = Set/get the pathname used to name the process. + /** + * Specify the full path or relative path, or just the executable + * name for the process. If this is set, then will be used to + * create the process instead of argv[0] set in the command + * line. This is here so that you can supply something other than + * executable name as argv[0]. + */ + void process_name (const ACE_TCHAR *name); + + /// Return the process_name. If the set + /// method is not called, this method will return argv[0]. + const ACE_TCHAR *process_name (void); + + // = Set/get creation flags. + /// Get the creation flags. + /// Set the creation flags. + u_long creation_flags (void) const; + void creation_flags (u_long); + + // = uses these operations to retrieve option values. + + /// Current working directory. Returns "" if nothing has been set. + ACE_TCHAR *working_directory (void); + + /// Buffer of command-line options. Returns exactly what was passed + /// to this->command_line. If @arg max_len is not 0, receives the + /// maximum length of the command line buffer. + ACE_TCHAR *command_line_buf (int *max_len = 0); + + /** + * argv-style command-line options. Parses and modifies the string + * created from . All spaces not in quotes ("" or + * '') are replaced with null (\0) bytes. An argv array is built + * and returned with each entry pointing to the start of + * null-terminated string. Returns { 0 } if nothing has been set. + */ + ACE_TCHAR * const *command_line_argv (void); + + /** + * Null-terminated buffer of null terminated strings. Each string + * is an environment assignment "VARIABLE=value". This buffer + * should end with two null characters. + */ + ACE_TCHAR *env_buf (void); + + // = Get/set process group. + /// On UNIX, these methods are used by the to + /// manage groups of processes. + pid_t getgroup (void) const; + pid_t setgroup (pid_t pgrp); + + /// Default is TRUE. + /// Allows disabling of handle inheritence. + int handle_inheritence (void); + void handle_inheritence (int); + + /// Cause the specified handle to be passed to a child process + /// when it runs a new program image. + /** + * The specified handle value will be included in the spawned + * process's command line as @arg +H @arg handle, if a new + * program is spawned (always on Win32; else if NO_EXEC is not + * set in creation flags). The passed handle value will be + * duplicated if on Win32 less capable than NT. + * @return 0 if success, -1 if failure. + */ + int pass_handle (ACE_HANDLE); + + /// Get a copy of the handles the ACE_Process_Options duplicated + /// for the spawned process. + /** + * Any handles created through duplication of those passed into + * @arg pass_handle are returned in @arg set. + * @return 0 if there were no handles to return; 1 if there were. + */ + int dup_handles (ACE_Handle_Set &set) const; + + /// Get a copy of the handles passed to the spawned process. This + /// will be the set of handles previously passed to @arg pass_handle(). + /** + * Any handles previously passed to @arg pass_handle are returned + * in @arg set. + * @return 0 if there were no handles to return; 1 if there were. + */ + int passed_handles (ACE_Handle_Set &set) const; + + /// Set value for avoid_zombies (has no real effect except on *nix). + /// Get current value for avoid_zombies. + void avoid_zombies (int); + int avoid_zombies (void); + +#if defined (ACE_WIN32) + // = Non-portable accessors for when you "just have to use them." + + /// Used for setting and getting. + ACE_TEXT_STARTUPINFO *startup_info (void); + + /// Get the process_attributes. Returns NULL if + /// set_process_attributes has not been set. + LPSECURITY_ATTRIBUTES get_process_attributes (void) const; + + /// If this is called, a non-null process attributes is sent to + /// CreateProcess. + LPSECURITY_ATTRIBUTES set_process_attributes (void); + + /// Get the thread_attributes. Returns NULL if set_thread_attributes + /// has not been set. + LPSECURITY_ATTRIBUTES get_thread_attributes (void) const; + + /// If this is called, a non-null thread attributes is sent to + /// CreateProcess. + LPSECURITY_ATTRIBUTES set_thread_attributes (void); + +#else /* All things not WIN32 */ + + /// argv-style array of environment settings. + ACE_TCHAR *const *env_argv (void); + + // = Accessors for the standard handles. + ACE_HANDLE get_stdin (void); + ACE_HANDLE get_stdout (void); + ACE_HANDLE get_stderr (void); + + // = Set/get real & effective user & group id associated with user. + int setreugid (const ACE_TCHAR* user); + void setruid (uid_t id); + void seteuid (uid_t id); + void setrgid (uid_t id); + void setegid (uid_t id); + uid_t getruid (void); + uid_t geteuid (void); + uid_t getrgid (void); + uid_t getegid (void); +#endif /* ACE_WIN32 */ +protected: + +#if !defined (ACE_HAS_WINCE) + /// Add to environment_buf_ and adjust + /// environment_argv_. is the strlen of . + int setenv_i (ACE_TCHAR *assignment, int len); + + /// Whether the child process inherits the current process + /// environment. + int inherit_environment_; +#endif /* !ACE_HAS_WINCE */ + + /// Default 0. + u_long creation_flags_; + + /// Avoid zombies for spawned processes. + int avoid_zombies_; + +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + /// Helper function to grab win32 environment and stick it in + /// environment_buf_ using this->setenv_i. + void inherit_environment (void); + + /// Ensures once only call to inherit environment. + int environment_inherited_; + + ACE_TEXT_STARTUPINFO startup_info_; + + /// Default TRUE. + BOOL handle_inheritence_; + + /// Pointer to security_buf1_. + LPSECURITY_ATTRIBUTES process_attributes_; + + /// Pointer to security_buf2_. + LPSECURITY_ATTRIBUTES thread_attributes_; + + /// Data for process_attributes_. + SECURITY_ATTRIBUTES security_buf1_; + + /// Data for thread_attributes_. + SECURITY_ATTRIBUTES security_buf2_; + +#else /* !ACE_WIN32 */ + ACE_HANDLE stdin_; + ACE_HANDLE stdout_; + ACE_HANDLE stderr_; + + // = Real & effective user & group id's. + // These should be set to -1 to leave unchanged (default). + uid_t ruid_; + uid_t euid_; + uid_t rgid_; + uid_t egid_; +#endif /* ACE_WIN32 */ + +#if !defined (ACE_HAS_WINCE) + /// Is 1 if stdhandles was called. + int set_handles_called_; + + /// Pointer into environment_buf_. This should point to the next + /// free spot. + int environment_buf_index_; + + /// Pointer to environment_argv_. + int environment_argv_index_; + + /// Pointer to buffer of the environment settings. + ACE_TCHAR *environment_buf_; + + /// Size of the environment buffer. Configurable + int environment_buf_len_; + + /// Pointers into environment_buf_. + ACE_TCHAR **environment_argv_; + + /// Maximum number of environment variables. Configurable + int max_environment_args_; + + /// Maximum index of environment_argv_ buffer + int max_environ_argv_index_; + + /// The current working directory. + ACE_TCHAR working_directory_[MAXPATHLEN + 1]; +#endif /* !ACE_HAS_WINCE */ + + /// Ensures command_line_argv is only calculated once. + int command_line_argv_calculated_; + + /// Pointer to buffer of command-line arguments. E.g., "-f foo -b bar". + ACE_TCHAR *command_line_buf_; + + /// Max length of command_line_buf_ + int command_line_buf_len_; + + /// Argv-style command-line arguments. + ACE_TCHAR *command_line_argv_[MAX_COMMAND_LINE_OPTIONS]; + + /// Process-group on Unix; unused on Win32. + pid_t process_group_; + + /// Set of handles that were passed in pass_handle (). + ACE_Handle_Set handles_passed_; + /// Results of duplicating handles passed in pass_handle (). + ACE_Handle_Set dup_handles_; + + /// Pathname for the process. Relative path or absolute path or just + /// the program name. + ACE_TCHAR process_name_[MAXPATHLEN + 1]; +}; + +/** + * @class ACE_Process + * + * @brief Process + * + * A Portable encapsulation for creating new processes. + * Notice that on UNIX platforms, if the is used, the + * is using the system call. It means that the + * should include a full path to the program file + * ( does not search the PATH). If is not used + * then, the is using the which searches for the + * program file in the PATH variable. + */ +class ACE_Export ACE_Process +{ +public: + + /// Default construction. Must use to start. + ACE_Process (void); + + /// Destructor. + virtual ~ACE_Process (void); + + /** + * Called just before in the . If this + * returns non-zero, the is aborted (and returns + * ACE_INVALID_PID). The default simply returns zero. + */ + virtual int prepare (ACE_Process_Options &options); + + /** + * Launch a new process as described by . Returns the + * process id of the newly spawned child on success or -1 on + * failure. + */ + virtual pid_t spawn (ACE_Process_Options &options); + + /// Called just after in the parent's context, if the + /// succeeds. The default is to do nothing. + virtual void parent (pid_t child); + + /** + * Called just after in the child's context. The + * default does nothing. This function is *not* called on Win32 + * because the process-creation scheme does not allow it. + */ + virtual void child (pid_t parent); + + /// Called by a that is removing this Process from + /// its table of managed Processes. Default is to do nothing. + virtual void unmanage (void); + + /** + * Wait for the process we've created to exit. If != 0, it + * points to an integer where the function store the exit status of + * child process to. If == then return 0 + * and don't block if the child process hasn't exited yet. A return + * value of -1 represents the operation failed, otherwise, + * the child process id is returned. + */ + pid_t wait (ACE_exitcode *status = 0, + int wait_options = 0); + + /** + * Timed wait for the process we've created to exit. A return value + * of -1 indicates that the something failed; 0 indicates that a + * timeout occurred. Otherwise, the child's process id is returned. + * If != 0, it points to an integer where the function + * stores the child's exit status. + * + * NOTE: on UNIX platforms this function uses , i.e., it + * overwrites any existing alarm. In addition, it steals all + * s during the timeout period, which will break another + * in the same process that's expecting + * to kick off process reaping. + */ + pid_t wait (const ACE_Time_Value &tv, + ACE_exitcode *status = 0); + + /// Send the process a signal. This is only portable to operating + /// systems that support signals, such as UNIX/POSIX. + int kill (int signum = SIGINT); + + /** + * Terminate the process abruptly using . + * This call doesn't give the process a chance to cleanup, so use it + * with caution... + */ + int terminate (void); + + /// Return the process id of the new child process. + pid_t getpid (void) const; + + /// Return the handle of the process, if it has one. + ACE_HANDLE gethandle (void) const; + + /// Return 1 if running; 0 otherwise. + int running (void) const; + + /// Return the Process' exit code + int exit_code (void) const; + + /// Set the Process' exit code (completely unrelated to whether the + /// Process has actually exited)! + void exit_code (int code); + + /// Close all the handles in the set obtained from the + /// @arg ACE_Process_Options::dup_handles object used to spawn + /// the process. + void close_dup_handles (void); + + /// Close all the handles in the set obtained from the + /// @arg ACE_Process_Options::passed_handles object used to spawn + /// the process. + void close_passed_handles (void); + +#if defined (ACE_WIN32) + PROCESS_INFORMATION process_info (void); +#endif /* ACE_WIN32 */ + +protected: +#if defined (ACE_WIN32) + PROCESS_INFORMATION process_info_; +#else /* ACE_WIN32 */ + /// Process id of the child. + pid_t child_id_; +#endif /* ACE_WIN32 */ + int exit_code_; + + /// Set of handles that were passed to the child process. + ACE_Handle_Set handles_passed_; + /// Handle duplicates made for the child process. + ACE_Handle_Set dup_handles_; +}; + + +/** + * @class ACE_Managed_Process + * + * @brief A process easily managed by ACE_Process_Manager. + * + * @arg ACE_Managed_Process is just an @arg ACE_Process with an + * @arg unmanage method that deletes the instance. + * This class is only valid for use as a dynamically-allocated object! + */ +class ACE_Export ACE_Managed_Process : public ACE_Process +{ +public: + ACE_Managed_Process (); + + virtual void unmanage (void); + // Cleanup by deleting . + +private: + virtual ~ACE_Managed_Process (void); + // Make sure that we're allocated dynamically! + + friend class ace_dewarn_gplusplus; + // Keep G++ happy... +}; + +#include "ace/SString.h" + +#if defined (__ACE_INLINE__) +#include "ace/Process.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_PROCESS_H */ diff --git a/ace/Threads/Process.i b/ace/Threads/Process.i new file mode 100644 index 00000000000..3b4f8e198d5 --- /dev/null +++ b/ace/Threads/Process.i @@ -0,0 +1,379 @@ +/* -*- C++ -*- */ +// $Id$ + +#if defined (ACE_WIN32) + +ACE_INLINE PROCESS_INFORMATION +ACE_Process::process_info (void) +{ + return process_info_; +} +#endif /* ACE_WIN32 */ + +ACE_INLINE ACE_HANDLE +ACE_Process::gethandle (void) const +{ +#if defined (ACE_WIN32) + return process_info_.hProcess; +#else + return ACE_HANDLE (child_id_); +#endif /* ACE_WIN32 */ +} + +ACE_INLINE pid_t +ACE_Process::getpid (void) + const +{ +#if defined (ACE_WIN32) + return process_info_.dwProcessId; +#else /* ACE_WIN32 */ + return child_id_; +#endif /* ACE_WIN32 */ +} + +ACE_INLINE pid_t +ACE_Process::wait (ACE_exitcode *status, + int wait_options) +{ + return ACE_OS::wait (this->getpid (), + status, + wait_options +#if defined (ACE_WIN32) + , process_info_.hProcess +#endif /* ACE_WIN32 */ + ); +} + +ACE_INLINE int +ACE_Process::kill (int signum) +{ + return ACE_OS::kill (this->getpid (), + signum); +} + +ACE_INLINE int +ACE_Process::terminate (void) +{ + return ACE::terminate_process (this->getpid ()); +} + +ACE_INLINE int +ACE_Process::exit_code (void) const +{ + return this->exit_code_; +} + +ACE_INLINE void +ACE_Process::exit_code (int code) +{ + this->exit_code_ = code; +} + +ACE_INLINE u_long +ACE_Process_Options::creation_flags (void) const +{ +#if defined (UNICODE) && defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + return creation_flags_ | CREATE_UNICODE_ENVIRONMENT; +#else + return creation_flags_; +#endif /* UNICODE */ +} + +ACE_INLINE void +ACE_Process_Options::creation_flags (u_long cf) +{ + creation_flags_ = cf; +} + +ACE_INLINE pid_t +ACE_Process_Options::getgroup (void) const +{ + return process_group_; +} + +ACE_INLINE pid_t +ACE_Process_Options::setgroup (pid_t pgrp) +{ + pid_t old = process_group_; + process_group_ = pgrp; + return old; +} + +ACE_INLINE int +ACE_Process_Options::handle_inheritence (void) +{ +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + return handle_inheritence_; +#else + ACE_NOTSUP_RETURN (0); // This is a benign error. +#endif /* ACE_WIN32 && ! ACE_HAS_WINCE */ +} + +ACE_INLINE void +ACE_Process_Options::handle_inheritence (int hi) +{ +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + handle_inheritence_ = hi; +#else + ACE_UNUSED_ARG (hi); + ACE_NOTSUP; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_INLINE int +ACE_Process_Options::avoid_zombies (void) +{ + return avoid_zombies_; +} +ACE_INLINE void +ACE_Process_Options::avoid_zombies (int avoid_zombies) +{ + avoid_zombies_ = avoid_zombies; +} + +#if defined (ACE_WIN32) + +ACE_INLINE ACE_TEXT_STARTUPINFO * +ACE_Process_Options::startup_info (void) +{ +#if !defined (ACE_HAS_WINCE) + return &startup_info_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_INLINE LPSECURITY_ATTRIBUTES +ACE_Process_Options::get_process_attributes (void) const +{ +#if !defined (ACE_HAS_WINCE) + return process_attributes_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_INLINE LPSECURITY_ATTRIBUTES +ACE_Process_Options::set_process_attributes (void) +{ +#if !defined (ACE_HAS_WINCE) + process_attributes_ = &security_buf1_; + return process_attributes_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_INLINE LPSECURITY_ATTRIBUTES +ACE_Process_Options::get_thread_attributes (void) const +{ +#if !defined (ACE_HAS_WINCE) + return thread_attributes_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_INLINE LPSECURITY_ATTRIBUTES +ACE_Process_Options::set_thread_attributes (void) +{ +#if !defined (ACE_HAS_WINCE) + thread_attributes_ = &security_buf2_; + return thread_attributes_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +#else /* !defined (ACE_WIN32) */ + +ACE_INLINE ACE_HANDLE +ACE_Process_Options::get_stdin (void) +{ + return stdin_; +} + +ACE_INLINE ACE_HANDLE +ACE_Process_Options::get_stdout (void) +{ + return stdout_; +} + +ACE_INLINE ACE_HANDLE +ACE_Process_Options::get_stderr (void) +{ + return stderr_; +} + +ACE_INLINE int +ACE_Process_Options::setreugid (const char* user) +{ +#if !defined (ACE_LACKS_PWD_FUNCTIONS) + struct passwd *ent = ACE_OS::getpwnam (user); + + if (ent != 0) + { + this->euid_ = ent->pw_uid; + this->ruid_ = ent->pw_uid; + this->egid_ = ent->pw_gid; + this->rgid_ = ent->pw_gid; + return 0; + } + else + return -1; +#else + ACE_UNUSED_ARG (user); + ACE_NOTSUP_RETURN (-1); +#endif /* ACE_LACKS_PWD_FUNCTIONS */ +} + +ACE_INLINE void +ACE_Process_Options::setruid (uid_t id) +{ + this->ruid_ = id; +} + +ACE_INLINE void +ACE_Process_Options::seteuid (uid_t id) +{ + this->euid_ = id; +} + +ACE_INLINE void +ACE_Process_Options::setrgid (uid_t id) +{ + this->rgid_ = id; +} + +ACE_INLINE void +ACE_Process_Options::setegid (uid_t id) +{ + this->egid_ = id; +} + +ACE_INLINE uid_t +ACE_Process_Options::getruid (void) +{ + return this->ruid_; +} + +ACE_INLINE uid_t +ACE_Process_Options::geteuid (void) +{ + return this->euid_; +} + +ACE_INLINE uid_t +ACE_Process_Options::getrgid (void) +{ + return this->rgid_; +} + +ACE_INLINE uid_t +ACE_Process_Options::getegid (void) +{ + return this->egid_; +} +#endif /* ACE_WIN32 */ + +ACE_INLINE ACE_TCHAR * +ACE_Process_Options::command_line_buf (int *max_lenp) +{ + if (max_lenp != 0) + *max_lenp = this->command_line_buf_len_; + return this->command_line_buf_; +} + +ACE_INLINE ACE_TCHAR * +ACE_Process_Options::working_directory (void) +{ +#if !defined (ACE_HAS_WINCE) + if (working_directory_[0] == '\0') + return 0; + else + return working_directory_; +#else + return 0; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_INLINE void +ACE_Process_Options::working_directory (const char *wd) +{ +#if !defined(ACE_HAS_WINCE) + ACE_OS::strcpy (working_directory_, ACE_TEXT_CHAR_TO_TCHAR (wd)); +#else + ACE_UNUSED_ARG (wd); +#endif /* !ACE_HAS_WINCE */ +} + +#if defined (ACE_HAS_WCHAR) +ACE_INLINE void +ACE_Process_Options::working_directory (const wchar_t *wd) +{ +#if !defined(ACE_HAS_WINCE) + ACE_OS::strcpy (working_directory_, ACE_TEXT_WCHAR_TO_TCHAR (wd)); +#else + ACE_UNUSED_ARG (wd); +#endif /* !ACE_HAS_WINCE */ +} +#endif /* ACE_HAS_WCHAR */ + +ACE_INLINE void +ACE_Process_Options::process_name (const ACE_TCHAR *p) +{ + ACE_OS::strcpy (this->process_name_, p); +} + +ACE_INLINE const ACE_TCHAR * +ACE_Process_Options::process_name (void) +{ + if (process_name_[0] == '\0') + this->process_name (this->command_line_argv ()[0]); + + return this->process_name_; +} + +ACE_INLINE void +ACE_Managed_Process::unmanage (void) +{ + delete this; +} + +#if defined (ACE_HAS_WINCE) +// Here is a collection of inline functions which are defined only +// under CE. They are not empty on most other platforms. + +ACE_INLINE int +ACE_Process_Options::setenv (ACE_TCHAR *envp[]) +{ + ACE_UNUSED_ARG (envp); + return -1; +} + +ACE_INLINE int +ACE_Process_Options::setenv (const ACE_TCHAR *format, ...) +{ + return -1; +} + +ACE_INLINE int +ACE_Process_Options::setenv (const ACE_TCHAR *variable_name, + const ACE_TCHAR *format, + ...) +{ + return -1; +} + +ACE_INLINE int +ACE_Process_Options::set_handles (ACE_HANDLE std_in, + ACE_HANDLE std_out, + ACE_HANDLE std_err) +{ + ACE_UNUSED_ARG (std_in); + ACE_UNUSED_ARG (std_out); + ACE_UNUSED_ARG (std_err); + return -1; +} + +#endif /* ACE_HAS_WINCE */ diff --git a/ace/Threads/Process_Manager.cpp b/ace/Threads/Process_Manager.cpp new file mode 100644 index 00000000000..46d5eb58c6d --- /dev/null +++ b/ace/Threads/Process_Manager.cpp @@ -0,0 +1,941 @@ +// $Id$ + +// Process_Manager.cpp +#include "ace/Synch_T.h" +#include "ace/Process.h" +#include "ace/Signal.h" +#include "ace/Process_Manager.h" +#include "ace/Object_Manager.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Process_Manager.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Process_Manager, "$Id$") + +#if defined (ACE_HAS_SIG_C_FUNC) +extern "C" void +ACE_Process_Manager_cleanup (void *instance, void *arg) +{ + ACE_Process_Manager::cleanup (instance, arg); +} +#endif + +void +ACE_Process_Manager::cleanup (void *, void *) +{ + ACE_Process_Manager::close_singleton (); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Process_Manager) + +// Singleton instance. +ACE_Process_Manager *ACE_Process_Manager::instance_ = 0; + +// Controls whether the is deleted when we shut down +// (we can only delete it safely if we created it!) +int ACE_Process_Manager::delete_instance_ = 0; + +ACE_Process_Descriptor::~ACE_Process_Descriptor (void) +{ +} + +void +ACE_Process_Descriptor::dump (void) const +{ + ACE_TRACE ("ACE_Process_Descriptor::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nproc_id_ = %d"), + this->process_->getpid( ))); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +void +ACE_Process_Manager::dump (void) const +{ + ACE_TRACE ("ACE_Process_Manager::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nmax_process_table_size_ = %d"), this->max_process_table_size_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncurrent_count_ = %d"), this->current_count_)); + + for (size_t i = 0; i < this->current_count_; i++) + this->process_table_[i].dump (); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Process_Descriptor::ACE_Process_Descriptor (void) + : process_ (0), + exit_notify_ (0) +{ + ACE_TRACE ("ACE_Process_Descriptor::ACE_Process_Descriptor"); +} + +ACE_Process_Manager * +ACE_Process_Manager::instance (void) +{ + ACE_TRACE ("ACE_Process_Manager::instance"); + + if (ACE_Process_Manager::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_Process_Manager::instance_ == 0) + { + ACE_NEW_RETURN (ACE_Process_Manager::instance_, + ACE_Process_Manager, + 0); + ACE_Process_Manager::delete_instance_ = 1; + + // Register with the Object_Manager so that the wrapper to + // delete the proactor will be called when Object_Manager is + // being terminated. + +#if defined ACE_HAS_SIG_C_FUNC + ACE_Object_Manager::at_exit (ACE_Process_Manager::instance_, + ACE_Process_Manager_cleanup, + 0); +#else + ACE_Object_Manager::at_exit (ACE_Process_Manager::instance_, + ACE_Process_Manager::cleanup, + 0); +#endif /* ACE_HAS_SIG_C_FUNC */ + + } + } + + return ACE_Process_Manager::instance_; +} + +ACE_Process_Manager * +ACE_Process_Manager::instance (ACE_Process_Manager *tm) +{ + ACE_TRACE ("ACE_Process_Manager::instance"); + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + ACE_Process_Manager *t = ACE_Process_Manager::instance_; + // We can't safely delete it since we don't know who created it! + ACE_Process_Manager::delete_instance_ = 0; + + // Register with the Object_Manager so that the wrapper to + // delete the proactor will be called when Object_Manager is + // being terminated. + +#if defined ACE_HAS_SIG_C_FUNC + ACE_Object_Manager::at_exit (ACE_Process_Manager::instance_, + ACE_Process_Manager_cleanup, + 0); +#else + ACE_Object_Manager::at_exit (ACE_Process_Manager::instance_, + ACE_Process_Manager::cleanup, + 0); +#endif /* ACE_HAS_SIG_C_FUNC */ + + ACE_Process_Manager::instance_ = tm; + return t; +} + +void +ACE_Process_Manager::close_singleton( void ) +{ + ACE_TRACE ("ACE_Process_Manager::close_singleton"); + + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance ())); + + if (ACE_Process_Manager::delete_instance_) + { + delete ACE_Process_Manager::instance_; + ACE_Process_Manager::instance_ = 0; + ACE_Process_Manager::delete_instance_ = 0; + } +} + +int +ACE_Process_Manager::resize (size_t size) +{ + ACE_TRACE ("ACE_Process_Manager::resize"); + + ACE_Process_Descriptor *temp; + + ACE_NEW_RETURN (temp, + ACE_Process_Descriptor[size], + -1); + + for (size_t i = 0; + i < this->current_count_; + i++) + // Structure assignment. + temp[i] = this->process_table_[i]; + + this->max_process_table_size_ = size; + + delete [] this->process_table_; + + this->process_table_ = temp; + return 0; +} + +// Create and initialize the table to keep track of the process pool. + +int +ACE_Process_Manager::open (size_t size, + ACE_Reactor *r) +{ + ACE_TRACE ("ACE_Process_Manager::open"); + +#if !defined (ACE_LACKS_SETPGID) + // Set up a process group so that the thread that opened this + // Manager will be able to put children into its own group and wait + // for them. + if (ACE_OS::setpgid (0, 0) == -1) + ACE_ERROR ((LM_WARNING, + ACE_LIB_TEXT ("%p.\n"), + ACE_LIB_TEXT ("ACE_Process_Manager::open: can't create a ") + ACE_LIB_TEXT ("process group; some wait functions may fail"))); +#endif /* ACE_LACKS_SETPGID */ + + if (r) + { + ACE_Event_Handler::reactor (r); +#if !defined (ACE_WIN32) && !defined (ACE_PSOS) + // Register signal handler object. + if (reactor ()->register_handler + (SIGCHLD, this) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "register_handler", + 1)); +#endif // !defined(ACE_WIN32) && !defined (ACE_PSOS) + } + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (this->max_process_table_size_ < size) + this->resize (size); + return 0; +} + +// Initialize the synchronization variables. + +ACE_Process_Manager::ACE_Process_Manager (size_t size, + ACE_Reactor *r) + : ACE_Event_Handler (), + process_table_ (0), + max_process_table_size_ (0), + current_count_ (0), + default_exit_handler_ (0) +#if defined (ACE_HAS_THREADS) + , lock_ () +#endif /* ACE_HAS_THREADS */ +{ + ACE_TRACE ("ACE_Process_Manager::ACE_Process_Manager"); + + if (this->open (size, + r) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Process_Manager"))); +} + +// Close up and release all resources. + +int +ACE_Process_Manager::close (void) +{ + ACE_TRACE ("ACE_Process_Manager::close"); + +#if !defined (ACE_WIN32) + if (this->reactor ()) + { + this->reactor ()->remove_handler (this, 0); + this->reactor (0); + } +#endif /* !ACE_WIN32 */ + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (this->process_table_ != 0) + { + while (this->current_count_ > 0) + this->remove_proc (0); + + delete [] this->process_table_; + this->process_table_ = 0; + this->max_process_table_size_ = 0; + this->current_count_ = 0; + } + + if (this->default_exit_handler_ != 0) + this->default_exit_handler_->handle_close (ACE_INVALID_HANDLE,0); + this->default_exit_handler_ = 0; + + return 0; +} + +ACE_Process_Manager::~ACE_Process_Manager (void) +{ + ACE_TRACE ("ACE_Process_Manager::~ACE_Process_Manager"); + this->close (); +} + +#if !defined (ACE_WIN32) + +// This is called when the Reactor notices that a Process has exited. +// What has actually happened is a SIGCHLD invoked the +// routine, which fooled the Reactor into thinking that this routine +// needed to be called. Since we don't know which Process exited, we +// must reap as many exit statuses as are immediately available. + +int +ACE_Process_Manager::handle_input (ACE_HANDLE) +{ + ACE_TRACE ("ACE_Process_Manager::handle_input"); + + pid_t pid; + + do + pid = this->wait (0, + ACE_Time_Value::zero); + while (pid != 0 && pid != ACE_INVALID_PID); + + return 0; +} + +#endif /* !ACE_WIN32 */ + +// On Unix, this routine is called asynchronously when a SIGCHLD is +// received. We just tweak the reactor so that it'll call back our +// function, which allows us to handle Process exits +// synchronously. +// +// On Win32, this routine is called synchronously, and is passed the +// HANDLE of the Process that exited, so we can do all our work here. + +int +ACE_Process_Manager::handle_signal (int, + siginfo_t *si, + ucontext_t *) +{ +#if defined (ACE_WIN32) + ACE_HANDLE proc = si->si_handle_; + ACE_exitcode status = 0; + BOOL result = ::GetExitCodeProcess (proc, + &status); + if (result) + { + if (status != STILL_ACTIVE) + { + { + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, lock_, -1)); + + ssize_t i = this->find_proc (proc); +#if 0 + pid_t pid = i != -1 + ? process_table_[i].process_->getpid () + : ACE_INVALID_PID; +#endif + this->notify_proc_handler (i, status); + this->remove_proc (i); + } + return -1; // remove this HANDLE/Event_Handler combination + } + else + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Process still active") + ACE_LIB_TEXT (" -- shouldn't have been called yet!\n")), + 0); // return 0 : stay registered + } + else + { + // failed. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("GetExitCodeProcess failed")), + -1); // return -1: unregister + } +#else /* !ACE_WIN32 */ + ACE_UNUSED_ARG (si); + return reactor ()->notify + (this, + ACE_Event_Handler::READ_MASK); +#endif /* !ACE_WIN32 */ +} + +int +ACE_Process_Manager::register_handler (ACE_Event_Handler *eh, + pid_t pid) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (pid == ACE_INVALID_PID) + { + if (this->default_exit_handler_ != 0) + this->default_exit_handler_->handle_close + (ACE_INVALID_HANDLE, + 0); + this->default_exit_handler_ = eh; + return 0; + } + + ssize_t i = this->find_proc (pid); + + if (i == -1) + // set "process not found" error + return -1; + + ACE_Process_Descriptor &proc_desc = this->process_table_[i]; + + if (proc_desc.exit_notify_ != 0) + proc_desc.exit_notify_->handle_close + (ACE_INVALID_HANDLE, + 0); + proc_desc.exit_notify_ = eh; + return 0; +} + +// Create a new process. + +pid_t +ACE_Process_Manager::spawn (ACE_Process_Options &options) +{ + ACE_Process *process; + ACE_NEW_RETURN (process, + ACE_Managed_Process, + ACE_INVALID_PID); + + return spawn (process, options); +} + +// Create a new process. + +pid_t +ACE_Process_Manager::spawn (ACE_Process *process, + ACE_Process_Options &options) +{ + ACE_TRACE ("ACE_Process_Manager::spawn"); + + if (options.getgroup () == ACE_INVALID_PID) + options.setgroup (ACE_OS::getpid ()); + + pid_t pid = process->spawn (options); + + // Only include the pid in the parent's table. + if (pid == ACE_INVALID_PID + || pid == 0) + return pid; + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, + ace_mon, this->lock_, -1)); + + if (this->append_proc (process) == -1) + // bad news: spawned, but not registered in table. + return ACE_INVALID_PID; + + return pid; +} + +// Create N new processs. + +int +ACE_Process_Manager::spawn_n (size_t n, + ACE_Process_Options &options, + pid_t *child_pids) +{ + ACE_TRACE ("ACE_Process_Manager::spawn_n"); + + if (child_pids != 0) + for (size_t i = 0; + i < n; + ++i) + child_pids[i] = ACE_INVALID_PID; + + for (size_t i = 0; + i < n; + i++) + { + pid_t pid = this->spawn (options); + if (pid == ACE_INVALID_PID || pid == 0) + // We're in the child or something's gone wrong. + return pid; + else if (child_pids != 0) + child_pids[i] = pid; + } + + return 0; +} + +// Append a process into the pool (does not check for duplicates). +// Must be called with locks held. + +int +ACE_Process_Manager::append_proc (ACE_Process *proc) +{ + ACE_TRACE ("ACE_Process_Manager::append_proc"); + + // Try to resize the array to twice its existing size if we run out + // of space... + if (this->current_count_ >= this->max_process_table_size_ + && this->resize (this->max_process_table_size_ * 2) == -1) + return -1; + else + { + ACE_Process_Descriptor &proc_desc = + this->process_table_[this->current_count_]; + + proc_desc.process_ = proc; + proc_desc.exit_notify_ = 0; + +#if defined (ACE_WIN32) + // If we have a Reactor, then we're supposed to reap Processes + // automagically. Get a handle to this new Process and tell the + // Reactor we're interested in on it. + + ACE_Reactor *r = this->reactor (); + if (r != 0) + r->register_handler (this, + proc->gethandle ()); +#endif /* ACE_WIN32 */ + + this->current_count_++; + return 0; + } +} + +// Insert a process into the pool (checks for duplicates and doesn't +// allow them to be inserted twice). + +int +ACE_Process_Manager::insert_proc (ACE_Process *proc) +{ + ACE_TRACE ("ACE_Process_Manager::insert_proc"); + + // Check for duplicates and bail out if they're already + // registered... + if (this->find_proc (proc->getpid ()) != -1) + return -1; + + return this->append_proc (proc); +} + +// Remove a process from the pool. + +int +ACE_Process_Manager::remove (pid_t pid) +{ + ACE_TRACE ("ACE_Process_Manager::remove"); + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + ssize_t i = this->find_proc (pid); + + if (i != -1) + return this->remove_proc (i); + + // set "process not found" error + return -1; +} + +// Remove a process from the pool. Must be called with locks held. + +int +ACE_Process_Manager::remove_proc (size_t i) +{ + ACE_TRACE ("ACE_Process_Manager::remove_proc"); + + // If there's an exit_notify_ for this pid, call its + // method. + + if (this->process_table_[i].exit_notify_ != 0) + { + this->process_table_[i].exit_notify_->handle_close + (this->process_table_[i].process_->gethandle(), + 0); + this->process_table_[i].exit_notify_ = 0; + } + +#if defined (ACE_WIN32) + ACE_Reactor *r = this->reactor (); + if (r != 0) + r->remove_handler (this->process_table_[i].process_->gethandle (), + ACE_Event_Handler::DONT_CALL); +#endif /* ACE_WIN32 */ + + this->process_table_[i].process_->unmanage (); + + this->process_table_[i].process_ = 0; + + this->current_count_--; + + if (this->current_count_ > 0) + // Compact the table by moving the last item into the slot vacated + // by the index being removed (this is a structure assignment). + this->process_table_[i] = + this->process_table_[this->current_count_]; + + return 0; +} + +int +ACE_Process_Manager::terminate (pid_t pid) +{ + ACE_TRACE ("ACE_Process_Manager::terminate"); + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + // Check for duplicates and bail out if they're already + // registered... + ssize_t i = this->find_proc (pid); + + if (i == -1) + // set "no such process" error + return -1; + + int result = ACE::terminate_process (pid); + + if (result != -1) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + this->remove_proc (i); + return 0; + } + else + return -1; +} + +int +ACE_Process_Manager::terminate (pid_t pid, + int sig) +{ + ACE_TRACE ("ACE_Process_Manager::terminate"); + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + // Check for duplicates and bail out if they're already + // registered... + ssize_t i = this->find_proc (pid); + + if (i == -1) + // set "no such process" error + return -1; + + return ACE_OS::kill (pid, sig); +} + +// Locate the index in the table associated with . Must be +// called with the lock held. + +ssize_t +ACE_Process_Manager::find_proc (pid_t pid) +{ + ACE_TRACE ("ACE_Process_Manager::find_proc"); + + for (size_t i = 0; i < this->current_count_; ++i) + if (pid == this->process_table_[i].process_->getpid ()) + return i; + + return -1; +} + +#if defined (ACE_WIN32) +// Locate the index in the table associated with . Must be +// called with the lock held. + +ssize_t +ACE_Process_Manager::find_proc (ACE_HANDLE h) +{ + ACE_TRACE ("ACE_Process_Manager::find_proc"); + + for (size_t i = 0; i < this->current_count_; ++i) + if (h == this->process_table_[i].process_->gethandle ()) + return i; + + return -1; +} +#endif /* ACE_WIN32 */ + +// Wait for all the Processs to exit, or until elapses. +// Returns the number of Processes remaining, or -1 on an error. + +int +ACE_Process_Manager::wait (const ACE_Time_Value &timeout) +{ + ACE_TRACE ("ACE_Process_Manager::wait"); + + ACE_Time_Value until = timeout; + ACE_Time_Value remaining = timeout; + + if (until < ACE_Time_Value::max_time) + until += ACE_OS::gettimeofday (); + + while (current_count_ > 0) + { + pid_t pid = this->wait (0, remaining); + + if (pid == ACE_INVALID_PID) // wait() failed + return -1; + else if (pid == 0) // timeout + break; + + remaining = until < ACE_Time_Value::max_time + ? until - ACE_OS::gettimeofday () + : ACE_Time_Value::max_time; + + if (remaining <= ACE_Time_Value::zero) + break; + + // else Process terminated...wait for more... + } + return current_count_; +} + +// Collect a single child process' exit status. Store the exit code +// in * if non-zero. Call the appropriate exit_notify. If +// == 0, wait for any of the Process_Manager's children (or as +// near as possible -- on Unix, we might accidentally get some other +// Process_Manager's Process, or an unmanaged Process, or a child +// process started by some other means. + +pid_t +ACE_Process_Manager::wait (pid_t pid, + ACE_exitcode *status) +{ + ACE_TRACE ("ACE_Process_Manager::wait"); + + return this->wait (pid, + ACE_Time_Value::max_time, + status); +} + +// Collect a single child processes' exit status, unless +// elapses before the process exits. Same caveats about accidental +// Process reaping on Unix as above. + +pid_t +ACE_Process_Manager::wait (pid_t pid, + const ACE_Time_Value &timeout, + ACE_exitcode *status) +{ + ACE_TRACE ("ACE_Process_Manager::wait"); + + ACE_exitcode local_stat = 0; + if (status == 0) + status = &local_stat; + + *status = 0; + + ssize_t idx = -1; + ACE_Process *proc = 0; + + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (pid != 0) + { + idx = this->find_proc (pid); + if (idx == -1) + return ACE_INVALID_PID; + else + proc = process_table_[idx].process_; + } + + if (proc != 0) + pid = proc->wait (timeout, status); + else + { + // Wait for any Process spawned by this Process_Manager. +#if defined (ACE_WIN32) + HANDLE *handles; + + ACE_NEW_RETURN (handles, + HANDLE[current_count_], + ACE_INVALID_PID); + + for (size_t i = 0; + i < current_count_; + ++i) + handles[i] = + process_table_[i].process_->gethandle (); + + DWORD result = ::WaitForMultipleObjects (current_count_, + handles, + FALSE, + timeout == ACE_Time_Value::max_time + ? INFINITE + : timeout.msec ()); + if (result == WAIT_FAILED) + pid = ACE_INVALID_PID; + else if (result == WAIT_TIMEOUT) + pid = 0; + else + { + // Green Hills produces a warning that result >= WAIT_OBJECT_0 is + // a pointless comparison because WAIT_OBJECT_0 is zero and DWORD is + // unsigned long, so this test is skipped for Green Hills. + // Same for mingw. +# if defined (ghs) || defined (__MINGW32__) + ACE_ASSERT (result < WAIT_OBJECT_0 + current_count_); +# else + ACE_ASSERT (result >= WAIT_OBJECT_0 + && result < WAIT_OBJECT_0 + current_count_); +# endif + + idx = this->find_proc (handles[result - WAIT_OBJECT_0]); + + if (idx != -1) + { + pid = process_table_[idx].process_->getpid (); + result = ::GetExitCodeProcess (handles[result - WAIT_OBJECT_0], + status); + if (result == 0) + { + // failed! + this->remove_proc (idx); + pid = ACE_INVALID_PID; + } + } + else + { + // uh oh...handle removed from process_table_, even though + // we're holding a lock! + delete [] handles; + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("Process removed") + ACE_LIB_TEXT (" -- somebody's ignoring the lock!\n")), + -1); + } + } + + delete [] handles; +#else /* !defined(ACE_WIN32) */ + if (timeout == ACE_Time_Value::max_time) + pid = ACE_OS::waitpid (-(ACE_OS::getpid ()), + status, + 0); + else if (timeout == ACE_Time_Value::zero) + pid = ACE_OS::waitpid (-(ACE_OS::getpid ()), + status, + WNOHANG); + else + { + ACE_Time_Value wait_until = + timeout + ACE_OS::gettimeofday(); + + for (;;) + { + pid = ACE_OS::waitpid (-(ACE_OS::getpid()), + status, + WNOHANG); + if (pid != 0) + // "no such children" error, or got one! + break; + + ACE_Sig_Set alarm_or_child; + + alarm_or_child.sig_add (SIGALRM); + alarm_or_child.sig_add (SIGCHLD); + + ACE_Time_Value time_left = wait_until - ACE_OS::gettimeofday (); + + // if ACE_OS::ualarm doesn't have sub-second resolution: + time_left += ACE_Time_Value (0, 500000); + time_left.usec (0); + + if (time_left <= ACE_Time_Value::zero) { + pid = 0; + break; + } + + ACE_OS::ualarm (time_left); + ACE_OS::sigwait (alarm_or_child); + } + } +#endif /* !defined (ACE_WIN32) */ + } + + if (pid != ACE_INVALID_PID && pid != 0) + { + if (proc == 0) + { + idx = this->find_proc (pid); + if (idx == -1) + { + // oops, reaped an unmanaged process! + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("(%P|%t) oops, reaped unmanaged %d\n"), + pid)); + return pid; + } + else + proc = process_table_[idx].process_; + } + else + ACE_ASSERT (pid == proc->getpid ()); + + this->notify_proc_handler (idx, + *status); + this->remove_proc (idx); + } + + return pid; +} + +// Legacy method: + +int +ACE_Process_Manager::reap (pid_t pid, + ACE_exitcode *stat_loc, + int options) +{ + ACE_TRACE ("ACE_Process_Manager::reap"); + + return this->wait (pid, + (ACE_BIT_ENABLED (options, WNOHANG) + ? ACE_Time_Value::zero + : ACE_Time_Value::max_time), + stat_loc); +} + +// Notify either the process-specific handler or the generic handler. +// If process-specific, call handle_close on the handler. Returns 1 +// if process found, 0 if not. Must be called with locks held. + +int +ACE_Process_Manager::notify_proc_handler (size_t i, + ACE_exitcode exit_code) +{ + if (i < current_count_) + { + ACE_Process_Descriptor &proc_desc = + this->process_table_[i]; + + proc_desc.process_->exit_code (exit_code); + + if (proc_desc.exit_notify_ != 0) + proc_desc.exit_notify_->handle_exit (proc_desc.process_); + else if (this->default_exit_handler_ != 0 + && this->default_exit_handler_->handle_exit (proc_desc.process_) < 0) + { + this->default_exit_handler_->handle_close + (ACE_INVALID_HANDLE, + 0); + this->default_exit_handler_ = 0; + } + return 1; + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("(%P:%t|%T) ACE_Process_Manager::notify_proc_handler:"), + ACE_LIB_TEXT (" unknown/unmanaged process reaped\n"))); + return 0; + } +} diff --git a/ace/Threads/Process_Manager.h b/ace/Threads/Process_Manager.h new file mode 100644 index 00000000000..dade19e1001 --- /dev/null +++ b/ace/Threads/Process_Manager.h @@ -0,0 +1,398 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Process_Manager.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + + +#ifndef ACE_PROCESS_MANAGER_H +#define ACE_PROCESS_MANAGER_H +#include "ace/pre.h" + +#include "ace/Synch.h" +#include "ace/Reactor.h" +#include "ace/Event_Handler.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Process.h" + +/** + * @class ACE_Process_Descriptor + * + * @brief Information describing each process that's controlled by an + * . + */ +class ACE_Export ACE_Process_Descriptor +{ +private: + friend class ACE_Process_Manager; + + /// Default ctor/dtor. + ACE_Process_Descriptor (void); + ~ACE_Process_Descriptor (void); + + /// Describes the process itself. + ACE_Process *process_; + + /// function to call when process exits + ACE_Event_Handler *exit_notify_; + + /// Dump the state of an object. + void dump (void) const; +}; + +/** + * @class ACE_Process_Manager + * + * @brief Manages a group of processes. + * + * This class allows applications to control groups of processes, + * similar to how the controls groups of + * threads. Naturally, it doesn't work at all on platforms, such + * as VxWorks or pSoS, that don't support process. + * There are two (main) ways of using , + * depending on how involved you wish to be with the termination + * of managed es. If you just want es to + * go away when they're finished, simply register the + * with an : + * ACE_Process_Manager mgr( 100, some_reactor ) + * -or- + * ACE_Process_Manager mgr; + * ... + * mgr.open( 100, some_reactor ); + * Then, the will clean up after any + * es that it spawns. (On Unix, this means executing a + * wait(2) to collect the exit status -- and avoid zombie + * processes; on Win32, it means closing the process and thread + * HANDLEs that are created when CreateProcess is called.) + * If, on the other hand (and for some inexplicable reason) you + * want to explicitly invoke the terminated cleanup + * code, then *don't* register the with a + * Reactor, and be sure to call one of the + * functions whenever there might be + * managed es that have exited. + * Note that in either case, allows you to + * register "" to be called when a specific + * exits, or when any without a specific + * exits. When a exits, the + * appropriate 's is called; the + * passed is either the Process' HANDLE (on Win32), + * or its pid cast to an (on unix). + * It is also possible to call the + * functions even though the is registered with + * a . + * Note also that the wait functions are "sloppy" on Unix, + * because there's no good way to wait for a subset of the + * children of a process. The wait functions may end up + * collecting the exit status of a process that's not managed by + * the whose you invoked. It's best to + * only use a single , and to create all + * subprocesses by calling that 's + * method. + * Incidentally, when you register your with a + * its notification pipe is used to help "reap" the + * available exit statuses. Therefore, you must not use a + * whose notify pipe has been disabled. Here's the + * sequence of steps used to reap the exit statuses in this case: + * + The registers a signal handler for + * SIGCHLD. + * + The SIGCHLD handler, when invoked, uses the 's + * method to inform the to wake up. + * + Next, the calls the 's + * , this happens synchronously, not in + * sighandler-space. + * + The method collects all available exit + * statuses. + */ +class ACE_Export ACE_Process_Manager : protected ACE_Event_Handler +{ +public: + friend class ACE_Process_Control; + + enum + { + DEFAULT_SIZE = 100 + }; + + // = Initialization and termination methods. + /** + * Initialize an with a table containing up to + * processes. This table resizes itself automatically as + * needed. If a non-NULL is provided, this + * uses it to notify an application when a + * process it controls exits. By default, however, we don't use an + * . + */ + ACE_Process_Manager (size_t size = ACE_Process_Manager::DEFAULT_SIZE, + ACE_Reactor *reactor = 0); + + /** + * Initialize an with a table containing up to + * processes. This table resizes itself automatically as + * needed. If a non-NULL is provided, this + * uses it to notify an application when a + * process it controls exits. By default, however, we don't use an + * . + */ + int open (size_t size = DEFAULT_SIZE, + ACE_Reactor *r = 0); + + /// Release all resources. Do not wait for processes to exit. + int close (void); + + /// Destructor releases all resources and does not wait for processes + /// to exit. + virtual ~ACE_Process_Manager (void); + + // = Singleton accessors. + /// Get pointer to a process-wide . + static ACE_Process_Manager *instance (void); + + /// Set pointer to a process-wide and return + /// existing pointer. + static ACE_Process_Manager *instance (ACE_Process_Manager *); + + /// Delete the dynamically allocated singleton. + static void close_singleton (void); + + /// Cleanup method, used by the to destroy the + /// singleton. + static void cleanup (void *instance, void *arg); + + // = Process creation methods. + + /** + * Create a new process by passing to . On + * success, returns the process id of the child that was created. + * On failure, returns ACE_INVALID_PID. + */ + pid_t spawn (ACE_Process *proc, + ACE_Process_Options &options); + + /** + * Create a new process by passing to + * . On success, returns the process id of the + * child that was created. On failure, returns ACE_INVALID_PID. + */ + pid_t spawn (ACE_Process_Options &options); + + /** + * Create new processes by passing to + * , which is called times. If + * is non-0 it is expected to be an array of 's, which + * are filled in with the process ids of each newly created process. + * Returns 0 on success and -1 on failure. + */ + int spawn_n (size_t n, + ACE_Process_Options &options, + pid_t *child_pids = 0); + + // = Process synchronization operations. + + /** + * Block until there are no more child processes running that were + * ed by this . Unlike the call + * below, this method does not require a signal handler or + * because it simply blocks synchronously waiting + * for all the children managed by this to + * exit. Note that this does not return any status information + * about the success or failure of exiting child processes, although + * any registered exit_handlers are called. Returns 0 on success + * (and s the corresponding entries + * from the ; otherwise, returns -1 on failure. + */ + int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time); + + /** + * Wait up to for a single process to terminate. If + * pid==0, waits for any of the managed es (but see the + * note in the class documentation above for caveats about this -- + * "sloppy process cleanup on unix") If pid != 0, waits for that + * only. Returns the pid of the Process whose exit was handled, 0 + * if a timeout occurred, or ACE_INVALID_PID on error. + */ + pid_t wait (pid_t pid, + const ACE_Time_Value &timeout, + ACE_exitcode *status = 0); + + /** + * Wait indefinitely for a single process to terminate. If pid==0, + * waits for any of the managed es (but see the note in + * the class documentation for caveats about this -- "sloppy Process + * cleanup on unix") If pid != 0, waits for that only. + * Returns the pid of the process whose exit was handled, or + * ACE_INVALID_PID on error. + */ + pid_t wait (pid_t pid, + ACE_exitcode *status = 0); + + /** + * Reap the result of a single process by calling , + * therefore, this method is not portable to Win32. If the child is + * successfully reaped, is called automatically. This + * method does the same thing that the method directly above + * it does -- It's just here for backwards compatibility. + */ + int reap (pid_t pid = -1, + ACE_exitcode *stat_loc = 0, + int options = WNOHANG); + + // = Utility methods. + /** + * Register an Event_Handler to be called back when the specified + * process exits. If pid == ACE_INVALID_PID this handler is called + * when any process with no specific handler exits. + */ + int register_handler (ACE_Event_Handler *event_handler, + pid_t pid = ACE_INVALID_PID); + + /** + * Remove process from the table. This is called + * automatically by the method after it successfully reaped a + * signal. It's also possible to call this method + * directly from a signal handler, but don't call both and + * ! + */ + int remove (pid_t pid); + + /** + * Abruptly terminate a single process with id using the + * method. Note that this call is + * potentially dangerous to use since the process being terminated + * may not have a chance to cleanup before it shuts down. Returns 0 + * on success and -1 on failure. + */ + int terminate (pid_t pid); + + /// On OSs that support signals, send the signal to the specified + /// process. Returns 0 on success and -1 on failure. + int terminate (pid_t pid, + int sig); + + /// Return the number of managed Processes. + size_t managed (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + // = These methods allow a to be an . + + // As an , the automagically + // detects child Processes exiting and calls notify_proc_handler() + // and remove(). This means that you don't have to (shouldn't!) + // call the wait(...) methods yourself. + + // On Unix, we can't detect individual process termination very + // well; the best method is to catch SIGCHLD and then call the + // polling wait() function to collect any available exit statuses. + // However, we don't want to do this from within a signal handler + // because of the restrictions associated. Therefore (following the + // lead in examples/mumble) we open a bogus handle (to ACE_DEV_NULL) + // and register that handle with our Reactor. Then, when our + // SIGCHLD handler gets invoked, we tell the Reactor that the bogus + // handle is readable. That will cause the handle_input() function + // to be called once we're out of the interrupt context, and + // handle_input() collects exit statuses. + + // On Win32, we simply register ourself with the Reactor to deal + // with the Process handle becoming signaled. No muss, no fuss, no + // signal handler, and no dummy handle. + +#if !defined(ACE_WIN32) + /// Collect one (or more, on unix) process exit status. + virtual int handle_input (ACE_HANDLE proc); +#endif // !defined(ACE_WIN32) + + /** + * On Unix, this routine is called asynchronously when a SIGCHLD is + * received. We just tweak the reactor so that it'll call back our + * function, which allows us to handle Process exits + * synchronously. + * + * On Win32, this routine is called synchronously, and is passed the + * HANDLE of the Process that exited, so we can do all our work here + */ + virtual int handle_signal (int signum, + siginfo_t * = 0, + ucontext_t * = 0); + +private: + /// Resize the pool of Process_Descriptors. + int resize (size_t); + + /// Locate the index of the table slot occupied by . + /// Returns -1 if is not in the + ssize_t find_proc (pid_t process_id); + +#if defined (ACE_WIN32) + /// Locate the index of the table slot occupied by . + /// Returns ~0 if is not in the + ssize_t find_proc (ACE_HANDLE process_handle); +#endif /* ACE_WIN32 */ + + /// Insert a process in the table (checks for duplicates). Omitting + /// the process handle won't work on Win32... + int insert_proc (ACE_Process *process); + + /** + * Append information about a process, i.e., its in the + * . Each entry is added at the end, growing the + * table if necessary. + */ + int append_proc (ACE_Process *process); + + /// Actually removes the process at index from the table. This method + /// must be called with locks held. + int remove_proc (size_t n); + + /// If there's a specific handler for the Process at index in the + /// table, or there's a default handler, call it. + int notify_proc_handler (size_t n, + ACE_exitcode status); + + /// Vector that describes process state within the Process_Manager. + ACE_Process_Descriptor *process_table_; + + /// Maximum number of processes we can manage (should be dynamically + /// allocated). + size_t max_process_table_size_; + + /// Current number of processes we are managing. + size_t current_count_; + + /// This event handler is used to notify when a process we control + /// exits. + ACE_Event_Handler *default_exit_handler_; + + /// Singleton pointer. + static ACE_Process_Manager *instance_; + + /// Controls whether the is deleted when we shut + /// down (we can only delete it safely if we created it!) + static int delete_instance_; + +#if defined (ACE_HAS_THREADS) + /// This lock protects access/ops on . + ACE_Recursive_Thread_Mutex lock_; +#endif /* ACE_HAS_THREADS */ +}; + +#if defined (__ACE_INLINE__) +#include "ace/Process_Manager.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_PROCESS_MANAGER_H */ diff --git a/ace/Threads/Process_Manager.i b/ace/Threads/Process_Manager.i new file mode 100644 index 00000000000..c6ee1f25260 --- /dev/null +++ b/ace/Threads/Process_Manager.i @@ -0,0 +1,8 @@ +/* -*- C++ -*- */ +// $Id$ + +ACE_INLINE size_t +ACE_Process_Manager::managed (void) const +{ + return current_count_; +} diff --git a/ace/Threads/Process_Mutex.cpp b/ace/Threads/Process_Mutex.cpp new file mode 100644 index 00000000000..3930903967c --- /dev/null +++ b/ace/Threads/Process_Mutex.cpp @@ -0,0 +1,76 @@ +// $Id$ + +#include "ace/Process_Mutex.h" +#include "ace/Synch.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Process_Mutex.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Process_Mutex, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Process_Mutex) + +void +ACE_Process_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_Process_Mutex::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->lock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +#if defined (_ACE_USE_SV_SEM) +const ACE_TCHAR * +ACE_Process_Mutex::unique_name (void) +{ + // For all platforms other than Win32, we are going to create a + // machine-wide unique name if one is not provided by the user. On + // Win32, unnamed synchronization objects are acceptable. + ACE::unique_name (this, this->name_, ACE_UNIQUE_NAME_LEN); + return this->name_; +} +#endif /* _ACE_USE_SV_SEM */ + +ACE_Process_Mutex::ACE_Process_Mutex (const char *name, void *arg) +#if defined (_ACE_USE_SV_SEM) + : lock_ (name ? ACE_TEXT_CHAR_TO_TCHAR (name) :this->unique_name ()) +#else + : lock_ (USYNC_PROCESS, ACE_TEXT_CHAR_TO_TCHAR (name), (ACE_mutexattr_t *) arg) +#endif /* _ACE_USE_SV_SEM */ +{ +#if defined (_ACE_USE_SV_SEM) + ACE_UNUSED_ARG (arg); +#endif /* !_ACE_USE_SV_SEM */ +} + +#if defined (ACE_HAS_WCHAR) +ACE_Process_Mutex::ACE_Process_Mutex (const wchar_t *name, void *arg) +#if defined (_ACE_USE_SV_SEM) + : lock_ (name ? ACE_TEXT_WCHAR_TO_TCHAR (name): this->unique_name ()) +#else + : lock_ (USYNC_PROCESS, ACE_TEXT_WCHAR_TO_TCHAR (name), (ACE_mutexattr_t *) arg) +#endif /* _ACE_USE_SV_SEM */ +{ +#if defined (_ACE_USE_SV_SEM) + ACE_UNUSED_ARG (arg); +#endif /* _ACE_USE_SV_SEM */ +} +#endif /* ACE_HAS_WCHAR */ +ACE_Process_Mutex::~ACE_Process_Mutex (void) +{ +} + +// +// These are instantiated both with and without ACE_HAS_THREADS. +// +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) + +template class ACE_Guard; + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) + +#pragma instantiate ACE_Guard + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Threads/Process_Mutex.h b/ace/Threads/Process_Mutex.h new file mode 100644 index 00000000000..8167570f0aa --- /dev/null +++ b/ace/Threads/Process_Mutex.h @@ -0,0 +1,195 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Process_Mutex.h + * + * $Id$ + * + * A wrapper for mutexes that can be used across processes on the + * same host machine, as well as within a process, of course. + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_PROCESS_MUTEX_H +#define ACE_PROCESS_MUTEX_H + +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// To make it easier to carry the setting though this file as well as +// Process_Mutex.{cpp inl}, set a private macro here. +#ifdef _ACE_USE_SV_SEM +# undef _ACE_USE_SV_SEM +#endif /* _ACE_USE_SV_SEM */ +#if defined (ACE_HAS_SYSV_IPC) && !defined (ACE_USES_MUTEX_FOR_PROCESS_MUTEX) +# include "ace/SV_Semaphore_Complex.h" +# define _ACE_USE_SV_SEM +#else +# include "ace/Synch.h" +#endif /* ACE_HAS_SYSV_IPC && !ACE_USES_MUTEX_FOR_PROCESS_MUTEX */ + +/** + * @class ACE_Process_Mutex + * + * @brief A wrapper for mutexes that can be used across processes on + * the same host machine, as well as within a process, of + * course. + * + * @attention The mechanism upon which @c ACE_Process_Mutex is based + * can be configured at build time to be either @c ACE_SV_Semaphore_Complex + * (on platforms that support it) or @c ACE_Mutex. On platforms that + * require interprocess mutexes be allocated from shared memory (Pthreads + * and UI Threads are examples), @c ACE_SV_Semaphore_Complex provides a + * more reliable mechanism for implementing inter-process mutex than + * @c ACE_Mutex. However, at least on some platforms, + * @c ACE_SV_Semaphore_Complex is limited to a small number of + * objects by the underlying System V IPC kernel parameters. If you + * want to force use of @c ACE_Mutex as the underlying mechanism, set + * @c ACE_USES_MUTEX_FOR_PROCESS_MUTEX in your @c config.h file. + * Also, if you require the ability to do a timed @c acquire(), you must + * set @c ACE_USES_MUTEX_FOR_PROCESS_MUTEX, as timed acquire does not + * work with System V semaphores. + */ +class ACE_Export ACE_Process_Mutex +{ +public: + /** + * Create a Process_Mutex, passing in the optional @c name. + * + * @param name optional, null-terminated string containing the name of + * the object. Multiple users of the same @c ACE_Process_Mutex must use + * the same name to access the same object. If not specified, a name + * is generated. + * @param arg optional, attributes to be used to initialize the mutex. + * If using @c ACE_SV_Semaphore_Complex as the underlying mechanism, + * this argument is ignored. + */ + ACE_Process_Mutex (const char *name = 0, + void *arg = 0); + +#if defined (ACE_HAS_WCHAR) + /** + * Create a Process_Mutex, passing in the optional @c name. (@c wchar_t + * version) + * + * @param name optional, null-terminated string containing the name of + * the object. Multiple users of the same @c ACE_Process_Mutex must use + * the same name to access the same object. If not specified, a name + * is generated. + * @param arg optional, attributes to be used to initialize the mutex. + * If using @c ACE_SV_Semaphore_Complex as the underlying mechanism, + * this argument is ignored. + */ + ACE_Process_Mutex (const wchar_t *name, + void *arg = 0); +#endif /* ACE_HAS_WCHAR */ + + ~ACE_Process_Mutex (void); + + /** + * Explicitly destroy the mutex. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + * + * @return 0 on success; -1 on failure. + */ + int remove (void); + + /** + * Acquire lock ownership (wait on queue if necessary). + * + * @return 0 on success; -1 on failure. + */ + int acquire (void); + + /** + * Acquire lock ownership, but timeout if lock if hasn't been + * acquired by given time. + * + * @param tv the absolute time until which the caller is willing to + * wait to acquire the lock. + * + * @return 0 on success; -1 on failure. + */ + int acquire (ACE_Time_Value &tv); + + /** + * Conditionally acquire lock (i.e., don't wait on queue). + * + * @return 0 on success; -1 on failure. If the lock could not be acquired + * because someone else already had the lock, @c errno is set to @c EBUSY. + */ + int tryacquire (void); + + /// Release lock and unblock a thread at head of queue. + int release (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire_read (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire_write (void); + + /** + * Conditionally acquire a lock (i.e., won't block). Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire a lock (i.e., won't block). Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here for consistency with the other synchronization + * APIs and usability with Lock adapters. Assumes the caller already has + * acquired the mutex and returns 0 in all cases. + */ + int tryacquire_write_upgrade (void); + +#if !defined (_ACE_USE_SV_SEM) + /// Return the underlying mutex. + const ACE_mutex_t &lock (void) const; +#endif /* !_ACE_USE_SV_SEM */ + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: +#if defined (_ACE_USE_SV_SEM) + /// If the user does not provide a name we generate a unique name in + /// this buffer. + ACE_TCHAR name_[ACE_UNIQUE_NAME_LEN]; + + /// Create and return the unique name. + const ACE_TCHAR *unique_name (void); + + /// We need this to get the right semantics... + ACE_SV_Semaphore_Complex lock_; +#else + ACE_Mutex lock_; +#endif /* _ACE_USE_SV_SEM */ +}; + +#if defined (__ACE_INLINE__) +#include "ace/Process_Mutex.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" + +#endif /* ACE_PROCESS_MUTEX_H */ diff --git a/ace/Threads/Process_Mutex.inl b/ace/Threads/Process_Mutex.inl new file mode 100644 index 00000000000..14c0af99e42 --- /dev/null +++ b/ace/Threads/Process_Mutex.inl @@ -0,0 +1,85 @@ +/* -*- C++ -*- */ +// $Id$ + +#if !defined (_ACE_USE_SV_SEM) +ACE_INLINE const ACE_mutex_t & +ACE_Process_Mutex::lock (void) const +{ +// ACE_TRACE ("ACE_Process_Mutex::lock"); + return this->lock_.lock (); +} +#endif /* !_ACE_USE_SV_SEM */ + +// Explicitly destroy the mutex. +ACE_INLINE int +ACE_Process_Mutex::remove (void) +{ + return this->lock_.remove (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_Process_Mutex::acquire (void) +{ + return this->lock_.acquire (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_Process_Mutex::acquire (ACE_Time_Value &tv) +{ +#if !defined (_ACE_USE_SV_SEM) + return this->lock_.acquire (tv); +#else + ACE_UNUSED_ARG (tv); + ACE_NOTSUP_RETURN (-1); +#endif /* !_ACE_USE_SV_SEM */ +} + +// Conditionally acquire lock (i.e., don't wait on queue). +ACE_INLINE int +ACE_Process_Mutex::tryacquire (void) +{ + return this->lock_.tryacquire (); +} + +// Release lock and unblock a thread at head of priority queue. +ACE_INLINE int +ACE_Process_Mutex::release (void) +{ + return this->lock_.release (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_Process_Mutex::acquire_read (void) +{ + return this->lock_.acquire_read (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_Process_Mutex::acquire_write (void) +{ + return this->lock_.acquire_write (); +} + +// Conditionally acquire a lock (i.e., won't block). +ACE_INLINE int +ACE_Process_Mutex::tryacquire_read (void) +{ + return this->lock_.tryacquire_read (); +} + +// Conditionally acquire a lock (i.e., won't block). +ACE_INLINE int +ACE_Process_Mutex::tryacquire_write (void) +{ + return this->lock_.tryacquire_write (); +} + +ACE_INLINE int +ACE_Process_Mutex::tryacquire_write_upgrade (void) +{ + return 0; +} diff --git a/ace/Threads/Process_Semaphore.cpp b/ace/Threads/Process_Semaphore.cpp new file mode 100644 index 00000000000..ebc036b509b --- /dev/null +++ b/ace/Threads/Process_Semaphore.cpp @@ -0,0 +1,91 @@ +// $Id$ + +#include "ace/Process_Semaphore.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Process_Semaphore.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Process_Semaphore, "$Id$") + +void +ACE_Process_Semaphore::dump (void) const +{ +// ACE_TRACE ("ACE_Process_Semaphore::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->lock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Process_Semaphore::ACE_Process_Semaphore (u_int count, + const ACE_TCHAR *name, + void *arg, + int max) +#if defined (ACE_WIN32) || defined (ACE_HAS_POSIX_SEM) || defined (ACE_PSOS) + : lock_ (count, USYNC_PROCESS, name, arg, max) +#else + : lock_ (name, ACE_SV_Semaphore_Complex::ACE_CREATE, count) +#endif /* ACE_WIN32 || ACE_HAS_POSIX_SEM || ACE_PSOS */ +{ + arg = arg; + max = max; +// ACE_TRACE ("ACE_Process_Semaphore::ACE_Process_Semaphore"); +} + +ACE_Process_Semaphore::~ACE_Process_Semaphore (void) +{ + // ACE_TRACE ("ACE_Process_Semaphore::~ACE_Process_Semaphore"); +} + +// Explicitly destroy the semaphore. + +int +ACE_Process_Semaphore::remove (void) +{ +// ACE_TRACE ("ACE_Process_Semaphore::remove"); + return this->lock_.remove (); +} + +// Block the thread until the semaphore count becomes +// greater than 0, then decrement it. + +int +ACE_Process_Semaphore::acquire (void) +{ +// ACE_TRACE ("ACE_Process_Semaphore::acquire"); + return this->lock_.acquire (); +} + +// Conditionally decrement the semaphore if count is greater +// than 0 (i.e., won't block). + +int +ACE_Process_Semaphore::tryacquire (void) +{ +// ACE_TRACE ("ACE_Process_Semaphore::tryacquire"); + return this->lock_.tryacquire (); +} + +// Increment the semaphore, potentially unblocking +// a waiting thread. + +int +ACE_Process_Semaphore::release (void) +{ +// ACE_TRACE ("ACE_Process_Semaphore::release"); + return this->lock_.release (); +} + +// +// These are instantiated both with and without ACE_HAS_THREADS. +// +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) + +// template class ACE_Guard; + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) + +// #pragma instantiate ACE_Guard + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Threads/Process_Semaphore.h b/ace/Threads/Process_Semaphore.h new file mode 100644 index 00000000000..208bafe8dc1 --- /dev/null +++ b/ace/Threads/Process_Semaphore.h @@ -0,0 +1,142 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Process_Semaphore.h + * + * $Id$ + * + * Wrapper for Dijkstra style general semaphores that work + * across processes. + * + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_PROCESS_SEMAPHORE_H +#define ACE_PROCESS_SEMAPHORE_H +#include "ace/pre.h" + +#include "ace/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if !(defined (ACE_WIN32) || defined (ACE_HAS_POSIX_SEM) || defined (ACE_PSOS)) +#include "ace/SV_Semaphore_Complex.h" +#endif /* !(ACE_WIN32 || ACE_HAS_POSIX_SEM || ACE_PSOS) */ + +/** + * @class ACE_Process_Semaphore + * + * @brief Wrapper for Dijkstra style general semaphores that work + * across processes. + */ +class ACE_Export ACE_Process_Semaphore +{ +public: + /// Initialize the semaphore, with an initial value of and a + /// maximum value of . + ACE_Process_Semaphore (u_int count = 1, // By default make this unlocked. + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7FFFFFFF); + + /** + * This method is a no-op, i.e., it doesn't remove the semaphore. + * If you want to remove the semaphore, you must call the + * method explicitly. + */ + ~ACE_Process_Semaphore (void); + + /** + * Explicitly destroy the semaphore. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Block the thread until the semaphore count becomes greater than + /// 0, then decrement it. + int acquire (void); + + /** + * Conditionally decrement the semaphore if count is greater than 0 + * (i.e., won't block). Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire (void); + + /// Increment the semaphore, potentially unblocking a waiting thread. + int release (void); + + /** + * Acquire semaphore ownership. This calls and is only + * here to make the interface consistent + * with the other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire semaphore ownership. This calls and is only + * here to make the interface consistent + * with the other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire semaphore (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire semaphore (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the semaphore using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + +#if defined (ACE_WIN32) || defined (ACE_HAS_POSIX_SEM) || defined (ACE_PSOS) + /// Return the underlying lock. + const ACE_sema_t &lock (void) const; +#endif /* ACE_WIN32 || ACE_HAS_POSIX_SEM || ACE_PSOS */ + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: +#if defined (ACE_WIN32) || defined (ACE_HAS_POSIX_SEM) || defined (ACE_PSOS) + ACE_Semaphore lock_; +#else + /// We need this to get the right semantics... + ACE_SV_Semaphore_Complex lock_; +#endif /* ACE_WIN32 || ACE_HAS_POSIX_SEM || ACE_PSOS */ +}; + +#if defined (__ACE_INLINE__) +#include "ace/Process_Semaphore.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_PROCESS_SEMAPHORE_H */ diff --git a/ace/Threads/Process_Semaphore.inl b/ace/Threads/Process_Semaphore.inl new file mode 100644 index 00000000000..8470291233c --- /dev/null +++ b/ace/Threads/Process_Semaphore.inl @@ -0,0 +1,61 @@ +/* -*- C++ -*- */ +// $Id$ + +#if defined (ACE_WIN32) || defined (ACE_HAS_POSIX_SEM) || defined (ACE_PSOS) +ACE_INLINE const ACE_sema_t & +ACE_Process_Semaphore::lock (void) const +{ +// ACE_TRACE ("ACE_Process_Semaphore::lock"); + return this->lock_.lock (); +} +#endif /* ACE_WIN32 || ACE_HAS_POSIX_SEM || ACE_PSOS */ + +// Acquire semaphore ownership. This calls and is only here +// to make the interface consistent with the +// other synchronization APIs. + +ACE_INLINE int +ACE_Process_Semaphore::acquire_read (void) +{ + return this->acquire (); +} + +// Acquire semaphore ownership. This calls and is only here +// to make the interface consistent with the +// other synchronization APIs. + +ACE_INLINE int +ACE_Process_Semaphore::acquire_write (void) +{ + return this->acquire (); +} + +// Conditionally acquire semaphore (i.e., won't block). This calls +// and is only here to make the +// interface consistent with the other synchronization APIs. + +ACE_INLINE int +ACE_Process_Semaphore::tryacquire_read (void) +{ + return this->tryacquire (); +} + +// Conditionally acquire semaphore (i.e., won't block). This calls +// and is only here to make the +// interface consistent with the other synchronization APIs. + +ACE_INLINE int +ACE_Process_Semaphore::tryacquire_write (void) +{ + return this->tryacquire (); +} + +// This is only here to make the +// interface consistent with the other synchronization APIs. +// Assumes the caller has already acquired the semaphore using one of +// the above calls, and returns 0 (success) always. +ACE_INLINE int +ACE_Process_Semaphore::tryacquire_write_upgrade (void) +{ + return 0; +} diff --git a/ace/Threads/RW_Process_Mutex.cpp b/ace/Threads/RW_Process_Mutex.cpp new file mode 100644 index 00000000000..1b0aee4eb28 --- /dev/null +++ b/ace/Threads/RW_Process_Mutex.cpp @@ -0,0 +1,59 @@ +// $Id$ + +#include "ace/RW_Process_Mutex.h" +#include "ace/Log_Msg.h" +#include "ace/ACE.h" + +ACE_RCSID(ace, RW_Process_Mutex, "$Id$") + +#if !defined (__ACE_INLINE__) +#include "ace/RW_Process_Mutex.inl" +#endif /* __ACE_INLINE__ */ + +ACE_ALLOC_HOOK_DEFINE(ACE_RW_Process_Mutex) + +const ACE_TCHAR * +ACE_RW_Process_Mutex::unique_name (void) +{ + ACE::unique_name (this, this->name_, ACE_UNIQUE_NAME_LEN); + return this->name_; +} + +ACE_RW_Process_Mutex::ACE_RW_Process_Mutex (const ACE_TCHAR *name, + int flags) + : lock_ (name ? name : this->unique_name (), flags +#if defined (ACE_WIN32) + , ACE_DEFAULT_OPEN_PERMS) +#else + , S_IRUSR | S_IWUSR) +#endif /* ACE_WIN32 */ +{ +// ACE_TRACE ("ACE_RW_Process_Mutex::ACE_RW_Process_Mutex"); +} + +ACE_RW_Process_Mutex::~ACE_RW_Process_Mutex (void) +{ +// ACE_TRACE ("ACE_RW_Process_Mutex::~ACE_RW_Process_Mutex"); +} + +void +ACE_RW_Process_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_RW_Process_Mutex::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->lock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +// +// These are instantiated both with and without ACE_HAS_THREADS. +// +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) + +// template class ACE_Guard; + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) + +// #pragma instantiate ACE_Guard + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Threads/RW_Process_Mutex.h b/ace/Threads/RW_Process_Mutex.h new file mode 100644 index 00000000000..77500d19aa3 --- /dev/null +++ b/ace/Threads/RW_Process_Mutex.h @@ -0,0 +1,114 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file RW_Process_Mutex.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_RW_PROCESS_MUTEX_H +#define ACE_RW_PROCESS_MUTEX_H +#include "ace/pre.h" + +#include "ace/File_Lock.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_RW_Process_Mutex + * + * @brief Wrapper for readers/writer locks that exist across processes. + * + * Note that because this class uses the + * as its implementation it only can be reliably + * used between separate processes, rather than threads in the + * same process. This isn't a limitation of ACE, it's simply + * the file lock semantics on UNIX and Win32. + */ +class ACE_Export ACE_RW_Process_Mutex +{ +public: + /// Create a readers/writer , passing in the optional + /// . If not specified, a name is generated. + ACE_RW_Process_Mutex (const ACE_TCHAR *name = 0, + int flags = O_CREAT|O_RDWR); + + ~ACE_RW_Process_Mutex (void); + + /** + * Explicitly destroy the mutex. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire (void); + + /** + * Conditionally acquire lock (i.e., don't wait on queue). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire (void); + + /// Release lock and unblock a thread at head of queue. + int release (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire_read (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire_write (void); + + /** + * Conditionally acquire a lock (i.e., won't block). Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire a lock (i.e., won't block). Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_write (void); + + /// Attempt to upgrade a read lock to a write lock. Returns 0 on + /// success, -1 on failure. + int tryacquire_write_upgrade (void); + + /// Return the underlying lock. + const ACE_File_Lock &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// If the user does not provide a name we generate a unique name in + /// this buffer. + ACE_TCHAR name_[ACE_UNIQUE_NAME_LEN]; + + /// Create and return the unique name. + const ACE_TCHAR *unique_name (void); + + /// We need this to get the readers/writer semantics... + ACE_File_Lock lock_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/RW_Process_Mutex.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_RW_PROCESS_MUTEX_H */ diff --git a/ace/Threads/RW_Process_Mutex.inl b/ace/Threads/RW_Process_Mutex.inl new file mode 100644 index 00000000000..cc59bb9fc17 --- /dev/null +++ b/ace/Threads/RW_Process_Mutex.inl @@ -0,0 +1,72 @@ +/* -*- C++ -*- */ +// $Id$ + +// Explicitly destroy the mutex. +ACE_INLINE int +ACE_RW_Process_Mutex::remove (void) +{ + return this->lock_.remove (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_RW_Process_Mutex::acquire (void) +{ + return this->lock_.acquire (); +} + +// Conditionally acquire lock (i.e., don't wait on queue). +ACE_INLINE int +ACE_RW_Process_Mutex::tryacquire (void) +{ + return this->lock_.tryacquire (); +} + +// Release lock and unblock a thread at head of priority queue. +ACE_INLINE int +ACE_RW_Process_Mutex::release (void) +{ + return this->lock_.release (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_RW_Process_Mutex::acquire_read (void) +{ + return this->lock_.acquire_read (); +} + +// Acquire lock ownership (wait on priority queue if necessary). +ACE_INLINE int +ACE_RW_Process_Mutex::acquire_write (void) +{ + return this->lock_.acquire_write (); +} + +// Conditionally acquire a lock (i.e., won't block). +ACE_INLINE int +ACE_RW_Process_Mutex::tryacquire_read (void) +{ + return this->lock_.tryacquire_read (); +} + +// Conditionally acquire a lock (i.e., won't block). +ACE_INLINE int +ACE_RW_Process_Mutex::tryacquire_write (void) +{ + return this->lock_.tryacquire_write (); +} + +// Conditionally upgrade a lock (i.e., won't block). +ACE_INLINE int +ACE_RW_Process_Mutex::tryacquire_write_upgrade (void) +{ + return this->lock_.tryacquire_write_upgrade (); +} + +ACE_INLINE const ACE_File_Lock & +ACE_RW_Process_Mutex::lock (void) const +{ +// ACE_TRACE ("ACE_RW_Process_Mutex::lock"); + return this->lock_; +} diff --git a/ace/Threads/Synch.cpp b/ace/Threads/Synch.cpp new file mode 100644 index 00000000000..6b81248db41 --- /dev/null +++ b/ace/Threads/Synch.cpp @@ -0,0 +1,902 @@ +// $Id$ + +#ifndef ACE_SYNCH_C +#define ACE_SYNCH_C + +#include "ace/Thread.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Synch_T.h" +#include "ace/Synch.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(ace, Synch, "$Id$") + +#if !defined (__ACE_INLINE__) +#include "ace/Synch.i" +#endif /* __ACE_INLINE__ */ + +ACE_ALLOC_HOOK_DEFINE(ACE_Null_Mutex) + +ACE_Lock::~ACE_Lock (void) +{ +} + +ACE_Adaptive_Lock::ACE_Adaptive_Lock (void) + : lock_ (0) +{ +} + +ACE_Adaptive_Lock::~ACE_Adaptive_Lock (void) +{ +} + +int +ACE_Adaptive_Lock::remove (void) +{ + return this->lock_->remove (); +} + +int +ACE_Adaptive_Lock::acquire (void) +{ + return this->lock_->acquire (); +} + +int +ACE_Adaptive_Lock::tryacquire (void) +{ + return this->lock_->tryacquire (); +} + +int +ACE_Adaptive_Lock::release (void) +{ + return this->lock_->release (); +} + +int +ACE_Adaptive_Lock::acquire_read (void) +{ + return this->lock_->acquire_read (); +} + +int +ACE_Adaptive_Lock::acquire_write (void) +{ + return this->lock_->acquire_write (); +} + +int +ACE_Adaptive_Lock::tryacquire_read (void) +{ + return this->lock_->tryacquire_read (); +} + +int +ACE_Adaptive_Lock::tryacquire_write (void) +{ + return this->lock_->tryacquire_write (); +} + +int +ACE_Adaptive_Lock::tryacquire_write_upgrade (void) +{ + return this->lock_->tryacquire_write_upgrade (); +} + +void +ACE_Adaptive_Lock::dump (void) const +{ + // return this->lock_->dump (); +} + +ACE_TSS_Adapter::ACE_TSS_Adapter (void *object, ACE_THR_DEST f) + : ts_obj_ (object), + func_ (f) +{ + // ACE_TRACE ("ACE_TSS_Adapter::ACE_TSS_Adapter"); +} + +void +ACE_TSS_Adapter::cleanup (void) +{ + // ACE_TRACE ("ACE_TSS_Adapter::cleanup"); + (*this->func_)(this->ts_obj_); // call cleanup routine for ts_obj_ +} + +extern "C" void +ACE_TSS_C_cleanup (void *object) +{ + // ACE_TRACE ("ACE_TSS_C_cleanup"); + if (object != 0) + { + ACE_TSS_Adapter *tss_adapter = (ACE_TSS_Adapter *) object; + // Perform cleanup on the real TS object. + tss_adapter->cleanup (); + // Delete the adapter object. + delete tss_adapter; + } +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Semaphore) + +void +ACE_Semaphore::dump (void) const +{ +// ACE_TRACE ("ACE_Semaphore::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Semaphore::ACE_Semaphore (u_int count, + int type, + const ACE_TCHAR *name, + void *arg, + int max) + : removed_ (0) +{ +// ACE_TRACE ("ACE_Semaphore::ACE_Semaphore"); + if (ACE_OS::sema_init (&this->semaphore_, count, type, + name, arg, max) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Semaphore::ACE_Semaphore"))); +} + +ACE_Semaphore::~ACE_Semaphore (void) +{ +// ACE_TRACE ("ACE_Semaphore::~ACE_Semaphore"); + + this->remove (); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Mutex) + +void +ACE_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_Mutex::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); +#if defined (CHORUS) + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("lockname_ = %s\n"), this->lockname_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("process_lock_ = %x\n"), this->process_lock_)); +#endif /* CHORUS */ + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Mutex::ACE_Mutex (int type, const ACE_TCHAR *name, ACE_mutexattr_t *arg) + : +#if defined (CHORUS) || defined(ACE_HAS_PTHREADS) || defined(ACE_HAS_STHREADS) + process_lock_ (0), + lockname_ (0), +#endif /* CHORUS */ + removed_ (0) +{ + // ACE_TRACE ("ACE_Mutex::ACE_Mutex"); + + // These platforms need process-wide mutex to be in shared memory. +#if defined(CHORUS) || defined (ACE_HAS_PTHREADS) || defined (ACE_HAS_STHREADS) + if (type == USYNC_PROCESS) + { + // Let's see if the shared memory entity already exists. + ACE_HANDLE fd = ACE_OS::shm_open (name, + O_RDWR | O_CREAT | O_EXCL, + ACE_DEFAULT_FILE_PERMS); + if (fd == ACE_INVALID_HANDLE) + { + if (errno == EEXIST) + fd = ACE_OS::shm_open (name, + O_RDWR | O_CREAT, + ACE_DEFAULT_FILE_PERMS); + else + return; + } + else + { + // We own this shared memory object! Let's set its size. + if (ACE_OS::ftruncate (fd, + sizeof (ACE_mutex_t)) == -1) + { + ACE_OS::close (fd); + return; + } + this->lockname_ = ACE_OS::strdup (name); + if (this->lockname_ == 0) + { + ACE_OS::close (fd); + return; + } + } + + this->process_lock_ = + (ACE_mutex_t *) ACE_OS::mmap (0, + sizeof (ACE_mutex_t), + PROT_RDWR, + MAP_SHARED, + fd, + 0); + ACE_OS::close (fd); + if (this->process_lock_ == MAP_FAILED) + return; + + if (this->lockname_ + && ACE_OS::mutex_init (this->process_lock_, + type, + name, + arg) != 0) + return; + } + // It is ok to fall through into the below if the + // USYNC_PROCESS flag is not enabled. +#endif /* CHORUS */ + if (ACE_OS::mutex_init (&this->lock_, + type, + name, + arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Mutex::ACE_Mutex"))); +} + +ACE_Mutex::~ACE_Mutex (void) +{ +// ACE_TRACE ("ACE_Mutex::~ACE_Mutex"); + this->remove (); +} + +ACE_Event::ACE_Event (int manual_reset, + int initial_state, + int type, + const ACE_TCHAR *name, + void *arg) + : removed_ (0) +{ + if (ACE_OS::event_init (&this->handle_, + manual_reset, + initial_state, + type, + name, + arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Event::ACE_Event"))); +} + +ACE_Event::~ACE_Event (void) +{ + this->remove (); +} + +int +ACE_Event::remove (void) +{ + int result = 0; + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::event_destroy (&this->handle_); + } + return result; +} + +ACE_event_t +ACE_Event::handle (void) const +{ + return this->handle_; +} + +void +ACE_Event::handle (ACE_event_t new_handle) +{ + this->handle_ = new_handle; +} + +int +ACE_Event::wait (void) +{ + return ACE_OS::event_wait (&this->handle_); +} + +int +ACE_Event::wait (const ACE_Time_Value *abstime, int use_absolute_time) +{ + return ACE_OS::event_timedwait (&this->handle_, + (ACE_Time_Value *) abstime, + use_absolute_time); +} + +int +ACE_Event::signal (void) +{ + return ACE_OS::event_signal (&this->handle_); +} + +int +ACE_Event::pulse (void) +{ + return ACE_OS::event_pulse (&this->handle_); +} + +int +ACE_Event::reset (void) +{ + return ACE_OS::event_reset (&this->handle_); +} + +void +ACE_Event::dump (void) const +{ + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Manual_Event::ACE_Manual_Event (int initial_state, + int type, + const char *name, + void *arg) + : ACE_Event (1, + initial_state, + type, + ACE_TEXT_CHAR_TO_TCHAR (name), + arg) +{ +} + +#if defined (ACE_HAS_WCHAR) +ACE_Manual_Event::ACE_Manual_Event (int initial_state, + int type, + const wchar_t *name, + void *arg) + : ACE_Event (1, + initial_state, + type, + ACE_TEXT_WCHAR_TO_TCHAR (name), + arg) +{ +} +#endif /* ACE_HAS_WCHAR */ + +void +ACE_Manual_Event::dump (void) const +{ + ACE_Event::dump (); +} + +ACE_Auto_Event::ACE_Auto_Event (int initial_state, + int type, + const char *name, + void *arg) + : ACE_Event (0, + initial_state, + type, + ACE_TEXT_CHAR_TO_TCHAR (name), + arg) +{ +} + +#if defined (ACE_HAS_WCHAR) +ACE_Auto_Event::ACE_Auto_Event (int initial_state, + int type, + const wchar_t *name, + void *arg) + : ACE_Event (0, + initial_state, + type, + ACE_TEXT_WCHAR_TO_TCHAR (name), + arg) +{ +} +#endif /* ACE_HAS_WCHAR */ + +void +ACE_Auto_Event::dump (void) const +{ + ACE_Event::dump (); +} + +#if defined (ACE_HAS_THREADS) + +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Mutex_Guard) + +void +ACE_Thread_Semaphore::dump (void) const +{ +// ACE_TRACE ("ACE_Thread_Semaphore::dump"); + + ACE_Semaphore::dump (); +} + +ACE_Thread_Semaphore::ACE_Thread_Semaphore (u_int count, + const ACE_TCHAR *name, + void *arg, + int max) + : ACE_Semaphore (count, USYNC_THREAD, name, arg, max) +{ +// ACE_TRACE ("ACE_Thread_Semaphore::ACE_Thread_Semaphore"); +} + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +void +ACE_Thread_Mutex_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +ACE_Recursive_Thread_Mutex::ACE_Recursive_Thread_Mutex (const ACE_TCHAR *name, + ACE_mutexattr_t *arg) + : removed_ (0) +{ + // ACE_TRACE ("ACE_Recursive_Thread_Mutex::ACE_Recursive_Thread_Mutex"); +#if defined (ACE_HAS_FSU_PTHREADS) && ! defined (ACE_WIN32) + // Initialize FSU pthreads package. If called more than once, + // pthread_init does nothing and so does no harm. + pthread_init (); +#endif /* ACE_HAS_FSU_PTHREADS && ! ACE_WIN32 */ + if (ACE_OS::recursive_mutex_init (&this->recursive_mutex_, + name, + arg) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("recursive_mutex_init"))); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Recursive_Thread_Mutex) + +ACE_Recursive_Thread_Mutex::~ACE_Recursive_Thread_Mutex (void) +{ + // ACE_TRACE ("ACE_Recursive_Thread_Mutex::~ACE_Recursive_Thread_Mutex"); + this->remove (); +} + +int +ACE_Recursive_Thread_Mutex::remove (void) +{ +// ACE_TRACE ("ACE_Recursive_Thread_Mutex::remove"); + int result = 0; + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::recursive_mutex_destroy (&this->recursive_mutex_); + } + return result; +} + +// The counter part of the following two functions for Win32 are +// located in file Synch.i +ACE_thread_t +ACE_Recursive_Thread_Mutex::get_thread_id (void) +{ + // ACE_TRACE ("ACE_Recursive_Thread_Mutex::get_thread_id"); +#if defined (ACE_HAS_RECURSIVE_MUTEXES) + // @@ The structure CriticalSection in Win32 doesn't hold the thread + // handle of the thread that owns the lock. However it is still not + // clear at this point how to translate a thread handle to its + // corresponding thread id. + errno = ENOTSUP; + return ACE_OS::NULL_thread; +#else + ACE_thread_t owner_id; + ACE_OS::mutex_lock (&this->recursive_mutex_.nesting_mutex_); + owner_id = this->recursive_mutex_.owner_id_; + ACE_OS::mutex_unlock (&this->recursive_mutex_.nesting_mutex_); + return owner_id; +#endif /* ACE_WIN32 */ +} + +int +ACE_Recursive_Thread_Mutex::get_nesting_level (void) +{ + // ACE_TRACE ("ACE_Recursive_Thread_Mutex::get_nesting_level"); +#if defined (ACE_HAS_WINCE) || defined (VXWORKS) || defined (ACE_PSOS) + ACE_NOTSUP_RETURN (-1); +#elif defined (ACE_HAS_RECURSIVE_MUTEXES) +# if defined (ACE_WIN32) + // This is really a Win32-ism... + return this->recursive_mutex_.RecursionCount; +# else + ACE_NOTSUP_RETURN (-1); +# endif /* ACE_HAS_RECURSIVE_MUTEXES */ +#else + int nesting_level = 0; + ACE_OS::mutex_lock (&this->recursive_mutex_.nesting_mutex_); + nesting_level = this->recursive_mutex_.nesting_level_; + ACE_OS::mutex_unlock (&this->recursive_mutex_.nesting_mutex_); + return nesting_level; +#endif /* !ACE_HAS_WINCE */ +} + +ACE_Recursive_Thread_Mutex::ACE_Recursive_Thread_Mutex (const ACE_Recursive_Thread_Mutex &) +{ +} + +int +ACE_Recursive_Thread_Mutex::acquire (void) +{ + return ACE_OS::recursive_mutex_lock (&this->recursive_mutex_); +} + +int +ACE_Recursive_Thread_Mutex::release (void) +{ + return ACE_OS::recursive_mutex_unlock (&this->recursive_mutex_); +} + +int +ACE_Recursive_Thread_Mutex::tryacquire (void) +{ + return ACE_OS::recursive_mutex_trylock (&this->recursive_mutex_); +} + +void +ACE_Recursive_Thread_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_Recursive_Thread_Mutex::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Condition_Thread_Mutex) + +void +ACE_Condition_Thread_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); +#if defined (ACE_WIN32) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("waiters = %d\n"), + this->cond_.waiters ())); +#endif /* ACE_WIN32 */ + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Condition_Thread_Mutex::ACE_Condition_Thread_Mutex (const ACE_Thread_Mutex &m, + const ACE_TCHAR *name, + void *arg) + : mutex_ ((ACE_Thread_Mutex &) m), + removed_ (0) +{ +#if defined (ACE_HAS_FSU_PTHREADS) +// Initialize FSU pthreads package. +// If called more than once, pthread_init does nothing +// and so does no harm. + pthread_init (); +#endif /* ACE_HAS_FSU_PTHREADS */ + +// ACE_TRACE ("ACE_Condition_Thread_Mutex::ACE_Condition_Thread_Mutex"); + if (ACE_OS::cond_init (&this->cond_, + (short) USYNC_THREAD, + name, + arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Condition_Thread_Mutex::ACE_Condition_Thread_Mutex"))); +} + +ACE_Condition_Thread_Mutex:: +ACE_Condition_Thread_Mutex (const ACE_Thread_Mutex &m, + ACE_Condition_Attributes &attributes, + const ACE_TCHAR *name, + void *arg) + : mutex_ ((ACE_Thread_Mutex &) m), + removed_ (0) +{ +#if defined (ACE_HAS_FSU_PTHREADS) +// Initialize FSU pthreads package. +// If called more than once, pthread_init does nothing +// and so does no harm. + pthread_init (); +#endif /* ACE_HAS_FSU_PTHREADS */ + +// ACE_TRACE ("ACE_Condition_Thread_Mutex::ACE_Condition_Thread_Mutex"); + if (ACE_OS::cond_init (&this->cond_, attributes.attributes_, + name, arg) != 0) + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Condition_Thread_Mutex::ACE_Condition_Thread_Mutex"))); +} + +ACE_Condition_Thread_Mutex::~ACE_Condition_Thread_Mutex (void) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::~ACE_Condition_Thread_Mutex"); + this->remove (); +} + +// Peform an "alertable" timed wait. If the argument == 0 +// then we do a regular , else we do a timed wait for up to +// using the function. + +int +ACE_Condition_Thread_Mutex::wait (void) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::wait"); + return ACE_OS::cond_wait (&this->cond_, &this->mutex_.lock_); +} + +int +ACE_Condition_Thread_Mutex::wait (ACE_Thread_Mutex &mutex, + const ACE_Time_Value *abstime) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::wait"); + return ACE_OS::cond_timedwait (&this->cond_, + &mutex.lock_, + (ACE_Time_Value *) abstime); +} + +int +ACE_Condition_Thread_Mutex::wait (const ACE_Time_Value *abstime) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::wait"); + return this->wait (this->mutex_, abstime); +} + +int +ACE_Condition_Thread_Mutex::signal (void) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::signal"); + return ACE_OS::cond_signal (&this->cond_); +} + +int +ACE_Condition_Thread_Mutex::broadcast (void) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::broadcast"); + return ACE_OS::cond_broadcast (&this->cond_); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Sub_Barrier) + +void +ACE_Sub_Barrier::dump (void) const +{ +// ACE_TRACE ("ACE_Sub_Barrier::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->barrier_finished_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("running_threads_ = %d"), this->running_threads_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Sub_Barrier::ACE_Sub_Barrier (u_int count, + ACE_Thread_Mutex &lock, + const ACE_TCHAR *name, + void *arg) + : barrier_finished_ (lock, name, arg), + running_threads_ (count) +{ +// ACE_TRACE ("ACE_Sub_Barrier::ACE_Sub_Barrier"); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Barrier) +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Barrier) +ACE_ALLOC_HOOK_DEFINE(ACE_Process_Barrier) + +void +ACE_Barrier::dump (void) const +{ +// ACE_TRACE ("ACE_Barrier::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->lock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("current_generation_ = %d"), this->current_generation_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncount_ = %d"), this->count_)); + this->sub_barrier_1_.dump (); + this->sub_barrier_2_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Barrier::ACE_Barrier (u_int count, + const ACE_TCHAR *name, + void *arg) + : lock_ (name, (ACE_mutexattr_t *) arg), + current_generation_ (0), + count_ (count), + sub_barrier_1_ (count, lock_, name, arg), + sub_barrier_2_ (count, lock_, name, arg) +{ +// ACE_TRACE ("ACE_Barrier::ACE_Barrier"); + this->sub_barrier_[0] = &this->sub_barrier_1_; + this->sub_barrier_[1] = &this->sub_barrier_2_; +} + +int +ACE_Barrier::wait (void) +{ +// ACE_TRACE ("ACE_Barrier::wait"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + + ACE_Sub_Barrier *sbp = + this->sub_barrier_[this->current_generation_]; + + // Check for shutdown... + if (sbp == 0) + return -1; + + if (sbp->running_threads_ == 1) + { + // We're the last running thread, so swap generations and tell + // all the threads waiting on the barrier to continue on their + // way. + + sbp->running_threads_ = this->count_; + // Swap generations. + this->current_generation_ = 1 - this->current_generation_; + sbp->barrier_finished_.broadcast (); + } + else + { + --sbp->running_threads_; + + // Block until all the other threads wait(). + while (sbp->running_threads_ != this->count_) + sbp->barrier_finished_.wait (); + } + + return 0; +} + +ACE_Thread_Barrier::ACE_Thread_Barrier (u_int count, const ACE_TCHAR *name) + : ACE_Barrier (count, name) +{ +// ACE_TRACE ("ACE_Thread_Barrier::ACE_Thread_Barrier"); +} + +void +ACE_Thread_Barrier::dump (void) const +{ +// ACE_TRACE ("ACE_Thread_Barrier::dump"); + ACE_Barrier::dump (); +} + +#if 0 +ACE_Process_Barrier::ACE_Process_Barrier (u_int count, const ACE_TCHAR *name) + : ACE_Barrier (count, USYNC_PROCESS, name) +{ +// ACE_TRACE ("ACE_Process_Barrier::ACE_Process_Barrier"); +} + +void +ACE_Process_Barrier::dump (void) const +{ +// ACE_TRACE ("ACE_Process_Barrier::dump"); + ACE_Barrier::dump (); +} + +template void +ACE_Process_Condition::dump (void) const +{ +// ACE_TRACE ("ACE_Process_Condition::dump"); + + ACE_Condition::dump (); +} + +template +ACE_Process_Condition::ACE_Process_Condition (MUTEX &m, + const ACE_TCHAR *name, + void *arg) + : ACE_Condition (m, USYNC_PROCESS, name, arg) +{ +// ACE_TRACE ("ACE_Process_Condition::ACE_Process_Condition"); +} +#endif /* 0 */ + +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Mutex) + +void +ACE_Thread_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_Thread_Mutex::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Thread_Mutex::~ACE_Thread_Mutex (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::~ACE_Thread_Mutex"); + this->remove (); +} + +ACE_Thread_Mutex::ACE_Thread_Mutex (const ACE_TCHAR *name, ACE_mutexattr_t *arg) + : removed_ (0) +{ +// ACE_TRACE ("ACE_Thread_Mutex::ACE_Thread_Mutex"); + + if (ACE_OS::thread_mutex_init (&this->lock_, + USYNC_THREAD, + name, + arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Thread_Mutex::ACE_Thread_Mutex"))); +} + +void +ACE_RW_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_RW_Mutex::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_RW_Mutex::ACE_RW_Mutex (int type, const ACE_TCHAR *name, void *arg) + : removed_ (0) +{ +// ACE_TRACE ("ACE_RW_Mutex::ACE_RW_Mutex"); + if (ACE_OS::rwlock_init (&this->lock_, type, name, arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_RW_Mutex::ACE_RW_Mutex"))); +} + +ACE_RW_Mutex::~ACE_RW_Mutex (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::~ACE_RW_Mutex"); + this->remove (); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_RW_Thread_Mutex) + +ACE_RW_Thread_Mutex::ACE_RW_Thread_Mutex (const ACE_TCHAR *name, + void *arg) + : ACE_RW_Mutex (USYNC_THREAD, name, arg) +{ +// ACE_TRACE ("ACE_RW_Thread_Mutex::ACE_RW_Thread_Mutex"); +} + +void +ACE_RW_Thread_Mutex::dump (void) const +{ +// ACE_TRACE ("ACE_RW_Thread_Mutex::dump"); + ACE_RW_Mutex::dump (); +} + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +// These are only instantiated with ACE_HAS_THREADS. +template class ACE_Guard; +template class ACE_Guard; +template class ACE_Read_Guard; +template class ACE_Read_Guard; +template class ACE_Write_Guard; +template class ACE_Write_Guard; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +// These are only instantiated with ACE_HAS_THREADS. +#pragma instantiate ACE_Guard +#pragma instantiate ACE_Guard +#pragma instantiate ACE_Read_Guard +#pragma instantiate ACE_Read_Guard +#pragma instantiate ACE_Write_Guard +#pragma instantiate ACE_Write_Guard +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + +#endif /* ACE_HAS_THREADS */ + +// +// These are instantiated both with and without ACE_HAS_THREADS. +// +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + +#endif /* ACE_SYNCH_C */ diff --git a/ace/Threads/Synch.h b/ace/Threads/Synch.h new file mode 100644 index 00000000000..10248793f6c --- /dev/null +++ b/ace/Threads/Synch.h @@ -0,0 +1,1753 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Synch.h + * + * $Id$ + * + * Wrapper Facades for various synchronization mechanisms. + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SYNCH_H +#define ACE_SYNCH_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward declarations. +/** + * @class ACE_Time_Value; + template class ACE_Condition; + */ +class ACE_Time_Value; + +/** + * @class ACE_Lock + * + * @brief This is the abstract base class that contains the uniform + * locking API that is supported by all the ACE synchronization + * mechanisms. + * + * This class is typically used in conjunction with the + * in order to provide a polymorphic + * interface to the ACE synchronization mechanisms (e.g., + * , , , etc). Note that + * the reason that all of ACE doesn't use polymorphic locks is + * that (1) they add ~20% extra overhead for virtual function + * calls and (2) objects with virtual functions can't be placed + * into shared memory. + */ +class ACE_Export ACE_Lock +{ +public: + /// CE needs a default ctor here. + ACE_Lock (void); + + /// Noop virtual destructor + virtual ~ACE_Lock (void); + + /** + * Explicitly destroy the lock. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + virtual int remove (void) = 0; + + /// Block the thread until the lock is acquired. Returns -1 on + /// failure. + virtual int acquire (void) = 0; + + /** + * Conditionally acquire the lock (i.e., won't block). Returns -1 + * on failure. If we "failed" because someone else already had the + * lock, is set to . + */ + virtual int tryacquire (void) = 0; + + /// Release the lock. Returns -1 on failure. + virtual int release (void) = 0; + + /** + * Block until the thread acquires a read lock. If the locking + * mechanism doesn't support read locks then this just calls + * . Returns -1 on failure. + */ + virtual int acquire_read (void) = 0; + + /** + * Block until the thread acquires a write lock. If the locking + * mechanism doesn't support read locks then this just calls + * . Returns -1 on failure. + */ + virtual int acquire_write (void) = 0; + + /** + * Conditionally acquire a read lock. If the locking mechanism + * doesn't support read locks then this just calls . + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + virtual int tryacquire_read (void) = 0; + + /** + * Conditionally acquire a write lock. If the locking mechanism + * doesn't support read locks then this just calls . + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + virtual int tryacquire_write (void) = 0; + + /** + * Conditionally try to upgrade a lock held for read to a write lock. + * If the locking mechanism doesn't support read locks then this just + * calls . Returns 0 on success, -1 on failure. + */ + virtual int tryacquire_write_upgrade (void) = 0; +}; + +/** + * @class ACE_Adaptive_Lock + * + * @brief An adaptive general locking class that defers the decision of + * lock type to run time. + * + * This class, as ACE_Lock, provide a set of general locking APIs. + * However, it defers our decision of what kind of lock to use + * to the run time and delegates all locking operations to the actual + * lock. Users must define a constructor in their subclass to + * initialize . + */ +class ACE_Export ACE_Adaptive_Lock : public ACE_Lock +{ +public: + /// You must also override the destructor function to match with how + /// you construct the underneath . + virtual ~ACE_Adaptive_Lock (void); + + // = Lock/unlock operations. + + virtual int remove (void); + virtual int acquire (void); + virtual int tryacquire (void); + virtual int release (void); + virtual int acquire_read (void); + virtual int acquire_write (void); + virtual int tryacquire_read (void); + virtual int tryacquire_write (void); + virtual int tryacquire_write_upgrade (void); + void dump (void) const; + +protected: + /** + * Create and initialize create the actual lcok used in the class. + * The default constructor simply set the to 0 (null). You + * must overwrite this method for this class to work. + */ + ACE_Adaptive_Lock (void); + + ACE_Lock *lock_; +}; + +/** + * @class ACE_Semaphore + * + * @brief Wrapper for Dijkstra style general semaphores. + */ +class ACE_Export ACE_Semaphore +{ +public: + // = Initialization and termination. + /// Initialize the semaphore, with initial value of "count". + ACE_Semaphore (u_int count = 1, // By default make this unlocked. + int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7fffffff); + + /// Implicitly destroy the semaphore. + ~ACE_Semaphore (void); + + /** + * Explicitly destroy the semaphore. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Block the thread until the semaphore count becomes + /// greater than 0, then decrement it. + int acquire (void); + + /** + * Block the thread until the semaphore count becomes greater than 0 + * (at which point it is decremented) or until times out (in + * which case -1 is returned and == ). Note that + * is assumed to be in "absolute" rather than "relative" time. The + * value of is updated upon return to show the actual + * (absolute) acquisition time. + * + * NOTE: Solaris threads do not support timed semaphores. + * Therefore, if you're running on Solaris you might want to + * consider using the ACE POSIX pthreads implementation instead, + * which can be enabled by compiling ACE with + * -DACE_HAS_PTHREADS, rather than -DACE_HAS_STHREADS or + * -DACE_HAS_POSIX_SEM. */ + int acquire (ACE_Time_Value &tv); + + /** + * If == 0 then call directly. Otherwise, Block + * the thread until the semaphore count becomes greater than 0 + * (at which point it is decremented) or until times out (in + * which case -1 is returned and == ). Note that + * <*tv> is assumed to be in "absolute" rather than "relative" time. + * The value of <*tv> is updated upon return to show the actual + * (absolute) acquisition time. + * + * NOTE: Solaris threads do not support timed semaphores. + * Therefore, if you're running on Solaris you might want to + * consider using the ACE POSIX pthreads implementation instead, + * which can be enabled by compiling ACE with + * -DACE_HAS_PTHREADS, rather than -DACE_HAS_STHREADS or + * -DACE_HAS_POSIX_SEM. */ + int acquire (ACE_Time_Value *tv); + + /** + * Conditionally decrement the semaphore if count is greater than 0 + * (i.e., won't block). Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire (void); + + /// Increment the semaphore by 1, potentially unblocking a waiting + /// thread. + int release (void); + + /// Increment the semaphore by , potentially + /// unblocking waiting threads. + int release (size_t release_count); + + /** + * Acquire semaphore ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire semaphore ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire semaphore (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire semaphore (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the semaphore using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + /// Return the underlying lock. + const ACE_sema_t &lock (void) const; + +protected: + ACE_sema_t semaphore_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Semaphore &); + ACE_Semaphore (const ACE_Semaphore &); +}; + +/** + * @class ACE_Null_Semaphore + * + * @brief Implement a do nothing , i.e., all the methods are + * no ops. + * + * Although the methods are no-ops, the return values are different for + * the blocking as opposed to timed acquires. The blocking version of + * acquire() is often used to serialize access to a critical section, + * whereas the timed version is often used to wait for another thread + * to update some condition or change some shared state. When using an + * ACE_Null_Semaphore, however, there's no other thread involved to + * change a state or condition (otherwise, a null semaphore would be + * inappropriate). Returning an error value signifies that the + * state or condition has not been (and can't be) changed, which is + * consistent with the behavior of the threaded case where a timeout + * occurs before the state or condition is changed. + */ +class ACE_Export ACE_Null_Semaphore +{ +public: + ACE_Null_Semaphore (u_int count = 1, // By default make this unlocked. + int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7fffffff); + ~ACE_Null_Semaphore (void); + /// Return 0. + int remove (void); + + /// Return 0. + int acquire (void); + + /// Return -1 with == . + int acquire (ACE_Time_Value &); + + /// Return -1 with == . + int acquire (ACE_Time_Value *); + + /// Return 0. + int tryacquire (void); + + /// Return 0. + int release (void); + + /// Return 0. + int release (size_t); + + /// Return 0. + int acquire_write (void); + + /// Return 0. + int tryacquire_write (void); + + /// Return 0. + int tryacquire_write_upgrade (void); + + /// Return 0. + int acquire_read (void); + + /// Return 0. + int tryacquire_read (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_RW_Mutex + * + * @brief Wrapper for readers/writer locks. + * + * These are most useful for applications that have many more + * parallel readers than writers... + */ +class ACE_Export ACE_RW_Mutex +{ +public: + /// Initialize a readers/writer lock. + ACE_RW_Mutex (int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Implicitly destroy a readers/writer lock + ~ACE_RW_Mutex (void); + + /** + * Explicitly destroy a readers/writer lock. Note that only one + * thread should call this method since it doesn't protect against + * race conditions. + */ + int remove (void); + + /// Acquire a read lock, but block if a writer hold the lock. + int acquire_read (void); + + /// Acquire a write lock, but block if any readers or a + /// writer hold the lock. + int acquire_write (void); + + /** + * Conditionally acquire a read lock (i.e., won't block). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire_read (void); + + /// Conditionally acquire a write lock (i.e., won't block). + int tryacquire_write (void); + + /** + * Conditionally upgrade a read lock to a write lock. This only + * works if there are no other readers present, in which case the + * method returns 0. Otherwise, the method returns -1 and sets + * to . Note that the caller of this method *must* + * already possess this lock as a read lock (but this condition is + * not checked by the current implementation). + */ + int tryacquire_write_upgrade (void); + + /** + * Note, for interface uniformity with other synchronization + * wrappers we include the method. This is implemented as + * a write-lock to safe... + */ + int acquire (void); + + /** + * Note, for interface uniformity with other synchronization + * wrappers we include the method. This is implemented + * as a write-lock to be safe... Returns -1 on failure. If we + * "failed" because someone else already had the lock, is + * set to . + */ + int tryacquire (void); + + /// Unlock a readers/writer lock. + int release (void); + + /// Return the underlying lock. + const ACE_rwlock_t &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Readers/writer lock. + ACE_rwlock_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_RW_Mutex &); + ACE_RW_Mutex (const ACE_RW_Mutex &); +}; + +/** + * @class ACE_Mutex + * + * @brief wrapper (valid in same process or across + * processes (depending on TYPE flag)). + */ +class ACE_Export ACE_Mutex +{ +public: + /// Initialize the mutex. + ACE_Mutex (int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + ACE_mutexattr_t *arg = 0); + + /// Implicitly destroy the mutex. + ~ACE_Mutex (void); + + /** + * Explicitly destroy the mutex. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire (void); + + /** + * Block the thread until the mutex is acquired or times out, + * in which case -1 is returned and == . Note that + * is assumed to be in "absolute" rather than "relative" time. + * The value of is updated upon return to show the actual + * (absolute) acquisition time. + */ + int acquire (ACE_Time_Value &tv); + + /** + * If == 0 then call directly. Otherwise, block + * the thread until the mutex is acquired or times out, in + * which case -1 is returned and == . Note that + * <*tv> is assumed to be in "absolute" rather than "relative" time. + * The value of <*tv> is updated upon return to show the actual + * (absolute) acquisition time. */ + int acquire (ACE_Time_Value *tv); + + /** + * Conditionally acquire lock (i.e., don't wait on queue). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire (void); + + /// Release lock and unblock a thread at head of queue. + int release (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the interface + * consistent with the other synchronization APIs. Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the interface + * consistent with the other synchronization APIs. Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here for consistency with the other synchronization + * APIs and usability with Lock adapters. Assumes the caller already has + * acquired the mutex and returns 0 in all cases. + */ + int tryacquire_write_upgrade (void); + + /// Return the underlying mutex. + const ACE_mutex_t &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + // = This should be protected but some C++ compilers complain... +public: +#if defined (CHORUS) || defined(ACE_HAS_PTHREADS) || defined(ACE_HAS_STHREADS) + /// This lock resides in shared memory. + ACE_mutex_t *process_lock_; + + /** + * Remember the name of the mutex if we created it so we can unlink + * it when we go away (only the actor that initialized the memory + * can destroy it). + */ + const ACE_TCHAR *lockname_; +#endif /* CHORUS || ACE_HAS_PTHREADS */ + + /// Mutex type supported by the OS. + ACE_mutex_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Mutex &); + ACE_Mutex (const ACE_Mutex &); +}; + +/** + * @class ACE_Null_Barrier + * + * @brief Implements "NULL barrier synchronization". + */ +class ACE_Export ACE_Null_Barrier +{ +public: + /// Initialize the barrier to synchronize threads. + ACE_Null_Barrier (u_int, + const char * = 0, + void * = 0); + + /// Default dtor. + ~ACE_Null_Barrier (void); + + /// Block the caller until all threads have called and + /// then allow all the caller threads to continue in parallel. + int wait (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Null_Barrier &); + ACE_Null_Barrier (const ACE_Null_Barrier &); +}; + +/** + * @class ACE_Null_Mutex + * + * @brief Implement a do nothing , i.e., all the methods are + * no ops. + */ +class ACE_Export ACE_Null_Mutex +{ +public: + ACE_Null_Mutex (const ACE_TCHAR * = 0); + ~ACE_Null_Mutex (void); + /// Return 0. + int remove (void); + + /// Return 0. + int acquire (void); + + /// Return -1 with == . + int acquire (ACE_Time_Value &timeout); + + /// Return -1 with == . + int acquire (ACE_Time_Value *timeout); + + /// Return 0. + int tryacquire (void); + + /// Return 0. + int release (void); + + /// Return 0. + int acquire_write (void); + + /// Return 0. + int tryacquire_write (void); + + /// Return 0. + int tryacquire_write_upgrade (void); + + /// Return 0. + int acquire_read (void); + + /// Return 0. + int tryacquire_read (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +class ACE_Export ACE_Noop_Token : public ACE_Null_Mutex +{ +public: + int renew (int = 0, ACE_Time_Value * =0); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Null_Condition + * + * @brief Implement a do nothing variable wrapper, + * i.e., all methods are no ops. This class is necessary since + * some C++ compilers are *very* lame... + */ +class ACE_Export ACE_Null_Condition +{ +public: + ACE_Null_Condition (const ACE_Null_Mutex &m, + const ACE_TCHAR * = 0, + void * = 0); + ~ACE_Null_Condition (void); + + /// Returns 0. + int remove (void); + + /// Returns -1 with == . + int wait (ACE_Time_Value * = 0); + + /// Returns 0. + int signal (void); + + /// Returns 0. + int broadcast (void); + ACE_Null_Mutex &mutex (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + ACE_Null_Mutex &mutex_; // Reference to mutex lock. + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Null_Condition &); + ACE_Null_Condition (const ACE_Null_Condition &); +}; + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +/** + * @class ACE_Null_Mutex_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * an ACE_Null_Mutex. + * + * This class is obsolete and should be replaced by + * ACE_Guard. + */ +class ACE_Export ACE_Null_Mutex_Guard +{ +public: + ACE_Null_Mutex_Guard (ACE_Null_Mutex &); + ~ACE_Null_Mutex_Guard (void); + int remove (void); + int locked (void); + int acquire (void); + int tryacquire (void); + int release (void); + void dump (void) const; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Null_Mutex_Guard &); + ACE_Null_Mutex_Guard (const ACE_Null_Mutex_Guard &); +}; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +/** + * @class ACE_TSS_Adapter + * + * @brief This class encapsulates a TSS object and its associated + * C++ destructor function. It is used by the ACE_TSS... + * methods (in Synch_T.cpp) in order to allow an extern + * "C" cleanup routine to be used. Needed by the "frigging" + * MVS C++ compiler. + * + * Objects of this class are stored in thread specific + * storage. ts_obj_ points to the "real" object and + * func_ is a pointer to the C++ cleanup function for ts_obj_. + */ +class ACE_Export ACE_TSS_Adapter +{ +public: + /// Initialize the adapter. + ACE_TSS_Adapter (void *object, ACE_THR_DEST f); + + /// Default dtor. + ~ACE_TSS_Adapter (void); + + /// Perform the cleanup operation. + void cleanup (void); + +//private: + + /// The real TS object. + void *ts_obj_; + + /// The real cleanup routine for ts_obj; + ACE_THR_DEST func_; +}; + +/** + * @class ACE_Event + * + * @brief A wrapper around the Win32 event locking mechanism. + * + * Portable implementation of an Event mechanism, which is + * native to Win32, but must be emulated on UNIX. Note that + * this only provides global naming and system-scope locking support + * on Win32 platforms. + */ +class ACE_Export ACE_Event +{ +public: + /// Constructor that creates event. + ACE_Event (int manual_reset = 0, + int initial_state = 0, + int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Implicitly destroy the event variable. + ~ACE_Event (void); + + /** + * Explicitly destroy the event variable. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Underlying handle to event. + ACE_event_t handle (void) const; + + /** + * Set the underlying handle to event. Note that this method assumes + * ownership of the and will close it down in . If + * you want the to stay open when is called make + * sure to call on the before closing it. You are + * responsible for the closing the existing before + * overwriting it. + */ + void handle (ACE_event_t new_handle); + + /** + * if MANUAL reset + * sleep till the event becomes signaled + * event remains signaled after wait() completes. + * else AUTO reset + * sleep till the event becomes signaled + * event resets wait() completes. + */ + int wait (void); + + /// Same as wait() above, but this one can be timed + /// is absolute time-of-day if if + /// is non-0, else it is relative time. + int wait (const ACE_Time_Value *abstime, + int use_absolute_time = 1); + + /** + * if MANUAL reset + * wake up all waiting threads + * set to signaled state + * else AUTO reset + * if no thread is waiting, set to signaled state + * if thread(s) are waiting, wake up one waiting thread and + * reset event + */ + int signal (void); + + /** + * if MANUAL reset + * wakeup all waiting threads and + * reset event + * else AUTO reset + * wakeup one waiting thread (if present) and + * reset event + */ + int pulse (void); + + /// Set to nonsignaled state. + int reset (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// The underlying handle. + ACE_event_t handle_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent copying. + ACE_Event (const ACE_Event& event); + const ACE_Event &operator= (const ACE_Event &rhs); +}; + +/** + * @class ACE_Manual_Event + * + * @brief Manual Events. + * + * Specialization of Event mechanism which wakes up all waiting + * threads on . Note that this only provides + * global naming and system-scope locking support on Win32 platforms. + */ +class ACE_Export ACE_Manual_Event : public ACE_Event +{ +public: + /// constructor which will create manual event + ACE_Manual_Event (int initial_state = 0, + int type = USYNC_THREAD, + const char *name = 0, + void *arg = 0); + +#if defined (ACE_HAS_WCHAR) + /// constructor which will create manual event (wchar_t version) + ACE_Manual_Event (int initial_state, + int type, + const wchar_t *name, + void *arg = 0); +#endif /* ACE_HAS_WCHAR */ + + /// Default dtor. + ~ACE_Manual_Event (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Auto_Event + * + * @brief Auto Events. + * + * Specialization of Event mechanism which wakes up one waiting + * thread on . Note that this only provides + * global naming and system-scope locking support on Win32 platforms. + */ +class ACE_Export ACE_Auto_Event : public ACE_Event +{ +public: + /// constructor which will create auto event + ACE_Auto_Event (int initial_state = 0, + int type = USYNC_THREAD, + const char *name = 0, + void *arg = 0); + +#if defined (ACE_HAS_WCHAR) + /// constructor which will create auto event (wchar_t version) + ACE_Auto_Event (int initial_state, + int type, + const wchar_t *name, + void *arg = 0); +#endif /* ACE_HAS_WCHAR */ + + /// Default dtor. + ~ACE_Auto_Event (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks + ACE_ALLOC_HOOK_DECLARE; +}; + +// ACE platform supports some form of threading. +#if !defined (ACE_HAS_THREADS) +/** + * @class ACE_Barrier + * + * @brief This is a no-op to make ACE "syntactically consistent." + */ +class ACE_Barrier +{ +public: + ACE_Barrier (u_int, const ACE_TCHAR * = 0, void * = 0) {} + ~ACE_Barrier (void) {} + int wait (void) { ACE_NOTSUP_RETURN (-1); } + void dump (void) const {} +}; +#else + /** + * @class ACE_Thread_Mutex + * + * @brief ACE_Thread_Mutex wrapper (only valid for threads in the same + * process). + * + * This implementation is optimized for locking threads that are + * in the same process. It maps to s on NT + * and with set to on UNIX. + * ACE_Thread_Mutex is recursive on some platforms (like + * Win32). However, on most platforms (like Solaris) it is not + * recursive. To be totally safe and portable, developers + * should use when they need a + * recursive mutex. + */ +class ACE_Export ACE_Thread_Mutex +{ + friend class ACE_Condition_Thread_Mutex; +public: + /// Constructor. + ACE_Thread_Mutex (const ACE_TCHAR *name = 0, + ACE_mutexattr_t *attributes = 0); + + /// Implicitly destroy the mutex. + ~ACE_Thread_Mutex (void); + + /** + * Explicitly destroy the mutex. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire (void); + + /** + * Block the thread until we acquire the mutex or until times + * out, in which case -1 is returned with == . Note + * that is assumed to be in "absolute" rather than "relative" + * time. The value of is updated upon return to show the + * actual (absolute) acquisition time. + */ + int acquire (ACE_Time_Value &tv); + + /** + * If == 0 the call directly. Otherwise, Block the + * thread until we acquire the mutex or until times out, in + * which case -1 is returned with == . Note that + * <*tv> is assumed to be in "absolute" rather than "relative" time. + * The value of <*tv> is updated upon return to show the actual + * (absolute) acquisition time. + */ + int acquire (ACE_Time_Value *tv); + + /** + * Conditionally acquire lock (i.e., don't wait on queue). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire (void); + + /// Release lock and unblock a thread at head of queue. + int release (void); + + /** + * Acquire mutex ownership. This calls and is only here + * to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire mutex ownership. This calls and is only here + * to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the mutex using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + + /// Return the underlying mutex. + const ACE_thread_mutex_t &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + // protected: + /// Mutex type that supports single-process locking efficiently. + ACE_thread_mutex_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Thread_Mutex &); + ACE_Thread_Mutex (const ACE_Thread_Mutex &); +}; + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +/** + * @class ACE_Thread_Mutex_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * an . + * + * This class is obsolete and should be replaced by + * ACE_Guard. + */ +class ACE_Export ACE_Thread_Mutex_Guard +{ +public: + /// Implicitly and automatically acquire the lock. + ACE_Thread_Mutex_Guard (ACE_Thread_Mutex &m, int block = 1); + + /// Implicitly release the lock. + ~ACE_Thread_Mutex_Guard (void); + + /// 1 if locked, 0 if couldn't acquire the lock (errno will contain + /// the reason for this). + int locked (void); + + /** + * Explicitly release the lock. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Explicitly acquire the lock. + int acquire (void); + + /** + * Conditionally acquire the lock (i.e., won't block). Returns -1 + * on failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire (void); + + /// Explicitly release the lock. + int release (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Reference to the mutex. + ACE_Thread_Mutex &lock_; + + /// Keeps track of whether we acquired the lock or failed. + int owner_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Thread_Mutex_Guard &); + ACE_Thread_Mutex_Guard (const ACE_Thread_Mutex_Guard &); +}; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +class ACE_Export ACE_Condition_Attributes +{ +public: + /// Constructor + ACE_Condition_Attributes (int type = ACE_DEFAULT_SYNCH_TYPE); + + /// Destructor + ~ACE_Condition_Attributes (void); + +private: + friend class ACE_Condition_Thread_Mutex; + + /// The attributes + ACE_condattr_t attributes_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Condition_Attributes &); + ACE_Condition_Attributes (const ACE_Condition_Attributes &); +}; + +/** + * @class ACE_Condition_Thread_Mutex + * + * @brief ACE_Condition variable wrapper written using ACE_Mutexes This + * allows threads to block until shared data changes state. + * A condition variable enables threads to atomically block and + * test the condition under the protection of a mutual exclu- + * sion lock (mutex) until the condition is satisfied. That is, + * the mutex must have been held by the thread before calling + * wait or signal on the condition. If the condition is false, + * a thread blocks on a condition variable and atomically + * releases the mutex that is waiting for the condition to + * change. If another thread changes the condition, it may wake + * up waiting threads by signaling the associated condition + * variable. The waiting threads, upon awakening, reacquire the + * mutex and re-evaluate the condition. + * + * This should be an instantiation of ACE_Condition but problems + * with compilers precludes this... + */ +class ACE_Export ACE_Condition_Thread_Mutex +{ +public: + /// Initialize the condition variable. + ACE_Condition_Thread_Mutex (const ACE_Thread_Mutex &m, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Initialize the condition variable. + ACE_Condition_Thread_Mutex (const ACE_Thread_Mutex &m, + ACE_Condition_Attributes &attributes, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Implicitly destroy the condition variable. + ~ACE_Condition_Thread_Mutex (void); + + /** + * Explicitly destroy the condition variable. Note that only one + * thread should call this method since it doesn't protect against + * race conditions. + */ + int remove (void); + + /** + * Block on condition, or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" semantics. Else, if + * != 0 and the call times out before the condition is signaled + * returns -1 and sets errno to ETIME. + */ + int wait (const ACE_Time_Value *abstime); + + /// Block on condition. + int wait (void); + + /** + * Block on condition or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" wait() semantics on the + * passed as a parameter (this is useful if you need to store the + * in shared memory). Else, if != 0 and the + * call times out before the condition is signaled returns -1 + * and sets errno to ETIME. + */ + int wait (ACE_Thread_Mutex &mutex, const ACE_Time_Value *abstime = 0); + + /// Signal one waiting thread. + int signal (void); + + /// Signal *all* waiting threads. + int broadcast (void); + + /// Returns a reference to the underlying mutex_; + ACE_Thread_Mutex &mutex (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Condition variable. + ACE_cond_t cond_; + + /// Reference to mutex lock. + ACE_Thread_Mutex &mutex_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Condition_Thread_Mutex &); + ACE_Condition_Thread_Mutex (const ACE_Condition_Thread_Mutex &); +}; + +/** + * @class ACE_Recursive_Thread_Mutex + * + * @brief Implement a C++ wrapper that allows nested acquisition and + * release of a mutex that occurs in the same thread. + */ +class ACE_Export ACE_Recursive_Thread_Mutex +{ +public: + /// Initialize a recursive mutex. + ACE_Recursive_Thread_Mutex (const ACE_TCHAR *name = 0, + ACE_mutexattr_t *arg = 0); + + /// Implicitly release a recursive mutex. + ~ACE_Recursive_Thread_Mutex (void); + + /** + * Implicitly release a recursive mutex. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /** + * Acquire a recursive mutex (will increment the nesting level and + * not deadmutex if the owner of the mutex calls this method more + * than once). + */ + int acquire (void); + + /** + * Conditionally acquire a recursive mutex (i.e., won't block). + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent + * with the other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent + * with the other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other + * synchronization APIs. Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other + * synchronization APIs. Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the mutex using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + + /** + * Releases a recursive mutex (will not release mutex until all the + * nesting level drops to 0, which means the mutex is no longer + * held). + */ + int release (void); + + /// Return the id of the thread that currently owns the mutex. + ACE_thread_t get_thread_id (void); + + /** + * Return the nesting level of the recursion. When a thread has + * acquired the mutex for the first time, the nesting level == 1. + * The nesting level is incremented every time the thread acquires + * the mutex recursively. + */ + int get_nesting_level (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + // = This method should *not* be public (they hold no locks...) + void set_thread_id (ACE_thread_t t); + + /// Recursive mutex. + ACE_recursive_thread_mutex_t recursive_mutex_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Recursive_Thread_Mutex &); + ACE_Recursive_Thread_Mutex (const ACE_Recursive_Thread_Mutex &); +}; + +/** + * @class ACE_RW_Thread_Mutex + * + * @brief Wrapper for readers/writer locks that exist within a process. + */ +class ACE_Export ACE_RW_Thread_Mutex : public ACE_RW_Mutex +{ +public: + ACE_RW_Thread_Mutex (const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Default dtor. + ~ACE_RW_Thread_Mutex (void); + + /** + * Conditionally upgrade a read lock to a write lock. This only + * works if there are no other readers present, in which case the + * method returns 0. Otherwise, the method returns -1 and sets + * to . Note that the caller of this method *must* + * already possess this lock as a read lock (but this condition is + * not checked by the current implementation). + */ + int tryacquire_write_upgrade (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Thread_Semaphore + * + * @brief Wrapper for Dijkstra style general semaphores that work + * only within one process. + */ +class ACE_Export ACE_Thread_Semaphore : public ACE_Semaphore +{ +public: + /// Initialize the semaphore, with an initial value of , + /// maximum value of , and unlocked by default. + ACE_Thread_Semaphore (u_int count = 1, // By default make this unlocked. + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7FFFFFFF); + + /// Default dtor. + ~ACE_Thread_Semaphore (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +struct ACE_Export ACE_Sub_Barrier +{ + // = Initialization. + ACE_Sub_Barrier (u_int count, + ACE_Thread_Mutex &lock, + const ACE_TCHAR *name = 0, + void *arg = 0); + + ~ACE_Sub_Barrier (void); + + ACE_Condition_Thread_Mutex barrier_finished_; + // True if this generation of the barrier is done. + + int running_threads_; + // Number of threads that are still running. + + void dump (void) const; + // Dump the state of an object. + + ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +/** + * @class ACE_Barrier + * + * @brief Implements "barrier synchronization". + * + * This class allows number of threads to synchronize + * their completion of (one round of) a task, which is known as + * "barrier synchronization". The implementation uses a + * "sub-barrier generation numbering" scheme to avoid overhead + * and to ensure that all threads wait to leave the barrier + * correct. This code is based on an article from SunOpsis + * Vol. 4, No. 1 by Richard Marejka + * (Richard.Marejka@canada.sun.com). + */ +class ACE_Export ACE_Barrier +{ +public: + /// Initialize the barrier to synchronize threads. + ACE_Barrier (u_int count, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Default dtor. + ~ACE_Barrier (void); + + /// Block the caller until all threads have called and + /// then allow all the caller threads to continue in parallel. + int wait (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Serialize access to the barrier state. + ACE_Thread_Mutex lock_; + + /// Either 0 or 1, depending on whether we are the first generation + /// of waiters or the next generation of waiters. + int current_generation_; + + /// Total number of threads that can be waiting at any one time. + int count_; + + /** + * We keep two , one for the first "generation" of + * waiters, and one for the next "generation" of waiters. This + * efficiently solves the problem of what to do if all the first + * generation waiters don't leave the barrier before one of the + * threads calls wait() again (i.e., starts up the next generation + * barrier). + */ + ACE_Sub_Barrier sub_barrier_1_; + ACE_Sub_Barrier sub_barrier_2_; + ACE_Sub_Barrier *sub_barrier_[2]; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Barrier &); + ACE_Barrier (const ACE_Barrier &); +}; + +#if 0 +// The following two classes are commented out since there doesn't +// appear to be a portable and robust means of implementing this +// functionality across platforms. If you know of a portable and +// robust way to implement this functionality please let us know. + +/** + * @class ACE_Process_Condition + * + * @brief ACE_Condition variable wrapper that works across processes. + */ +class ACE_Export ACE_Process_Condition +{ +public: + ACE_Process_Condition (MUTEX &m, const ACE_TCHAR *name = 0, void *arg = 0); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; +#endif /* 0 */ + +#if 0 +/** + * @class ACE_Process_Barrier + * + * @brief Implements "barrier synchronization" using ACE_Process_Mutexes! + * + * This class is just a simple wrapper for ACE_Barrier that + * selects the USYNC_PROCESS variant for the locks. + */ +class ACE_Export ACE_Process_Barrier : public ACE_Barrier +{ +public: + /// Create a Process_Barrier, passing in the optional . + ACE_Process_Barrier (u_int count, const ACE_TCHAR *name = 0); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; +#endif /* 0 */ + +/** + * @class ACE_Thread_Barrier + * + * @brief Implements "barrier synchronization" using ACE_Thread_Mutexes! + * + * This class is just a simple wrapper for ACE_Barrier that + * selects the USYNC_THREAD variant for the locks. + */ +class ACE_Export ACE_Thread_Barrier : public ACE_Barrier +{ +public: + /// Create a Thread_Barrier, passing in the optional . + ACE_Thread_Barrier (u_int count, const ACE_TCHAR *name = 0); + + /// Default dtor. + ~ACE_Thread_Barrier (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; +#endif /* ACE_HAS_THREADS */ + +#if defined (__ACE_INLINE__) +#include "ace/Threads/Synch.i" +#endif /* __ACE_INLINE__ */ + +// Include the templates here. +#include "ace/Threads/Synch_T.h" + +template +class ACE_Guard; + +ACE_TEMPLATE_SPECIALIZATION +/** + * @class ACE_Guard + * + * @brief Template specialization of for the + * . + * + * This specialization is useful since it helps to speedup + * performance of the "Null_Mutex" considerably. + */ +class ACE_Export ACE_Guard +{ +public: + // = Initialization and termination methods. + ACE_Guard (ACE_Null_Mutex &) {} + ACE_Guard (ACE_Null_Mutex &, int) {} +#if defined (ACE_WIN32) + ~ACE_Guard (void) {} +#endif /* ACE_WIN32 */ + + int acquire (void) { return 0; } + int tryacquire (void) { return 0; } + int release (void) { return 0; } + int locked (void) { return 1; } + int remove (void) { return 0; } + void dump (void) const {} + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Guard &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Guard (const ACE_Guard &)) +}; + +template +class ACE_Write_Guard; + +ACE_TEMPLATE_SPECIALIZATION +/** + * @class ACE_Write_Guard + * + */ +class ACE_Export ACE_Write_Guard : public ACE_Guard +{ +public: + ACE_Write_Guard (ACE_Null_Mutex &m) + : ACE_Guard (m) {} + ACE_Write_Guard (ACE_Null_Mutex &m, int blocked) + : ACE_Guard (m, blocked) {} + + int acquire_write (void) { return 0; } + int acquire (void) { return 0; } + int tryacquire_write (void) { return 0; } + int tryacquire (void) { return 0; } + void dump (void) const {} +}; + +template +class ACE_Read_Guard; + +ACE_TEMPLATE_SPECIALIZATION +/** + * @class ACE_Read_Guard + * + */ +class ACE_Export ACE_Read_Guard : public ACE_Guard +{ +public: + ACE_Read_Guard (ACE_Null_Mutex &m) + : ACE_Guard (m) {} + ACE_Read_Guard (ACE_Null_Mutex &m, int blocked) + : ACE_Guard (m, blocked) {} + + int acquire_read (void) { return 0; } + int acquire (void) { return 0; } + int tryacquire_read (void) { return 0; } + int tryacquire (void) { return 0; } + void dump (void) const {} +}; + +#if defined (ACE_LEGACY_MODE) +# include "ace/File_Lock.h" +# include "ace/Process_Semaphore.h" +# include "ace/Process_Mutex.h" +# include "ace/RW_Process_Mutex.h" +# include "ace/Test_and_Set.h" +#endif /* ACE_LEGACY_MODE */ + +#include "ace/post.h" +#endif /* ACE_SYNCH_H */ diff --git a/ace/Threads/Synch.h~ b/ace/Threads/Synch.h~ new file mode 100644 index 00000000000..f46053bef2b --- /dev/null +++ b/ace/Threads/Synch.h~ @@ -0,0 +1,1753 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Synch.h + * + * $Id$ + * + * Wrapper Facades for various synchronization mechanisms. + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SYNCH_H +#define ACE_SYNCH_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward declarations. +/** + * @class ACE_Time_Value; + template class ACE_Condition; + */ +class ACE_Time_Value; + +/** + * @class ACE_Lock + * + * @brief This is the abstract base class that contains the uniform + * locking API that is supported by all the ACE synchronization + * mechanisms. + * + * This class is typically used in conjunction with the + * in order to provide a polymorphic + * interface to the ACE synchronization mechanisms (e.g., + * , , , etc). Note that + * the reason that all of ACE doesn't use polymorphic locks is + * that (1) they add ~20% extra overhead for virtual function + * calls and (2) objects with virtual functions can't be placed + * into shared memory. + */ +class ACE_Export ACE_Lock +{ +public: + /// CE needs a default ctor here. + ACE_Lock (void); + + /// Noop virtual destructor + virtual ~ACE_Lock (void); + + /** + * Explicitly destroy the lock. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + virtual int remove (void) = 0; + + /// Block the thread until the lock is acquired. Returns -1 on + /// failure. + virtual int acquire (void) = 0; + + /** + * Conditionally acquire the lock (i.e., won't block). Returns -1 + * on failure. If we "failed" because someone else already had the + * lock, is set to . + */ + virtual int tryacquire (void) = 0; + + /// Release the lock. Returns -1 on failure. + virtual int release (void) = 0; + + /** + * Block until the thread acquires a read lock. If the locking + * mechanism doesn't support read locks then this just calls + * . Returns -1 on failure. + */ + virtual int acquire_read (void) = 0; + + /** + * Block until the thread acquires a write lock. If the locking + * mechanism doesn't support read locks then this just calls + * . Returns -1 on failure. + */ + virtual int acquire_write (void) = 0; + + /** + * Conditionally acquire a read lock. If the locking mechanism + * doesn't support read locks then this just calls . + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + virtual int tryacquire_read (void) = 0; + + /** + * Conditionally acquire a write lock. If the locking mechanism + * doesn't support read locks then this just calls . + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + virtual int tryacquire_write (void) = 0; + + /** + * Conditionally try to upgrade a lock held for read to a write lock. + * If the locking mechanism doesn't support read locks then this just + * calls . Returns 0 on success, -1 on failure. + */ + virtual int tryacquire_write_upgrade (void) = 0; +}; + +/** + * @class ACE_Adaptive_Lock + * + * @brief An adaptive general locking class that defers the decision of + * lock type to run time. + * + * This class, as ACE_Lock, provide a set of general locking APIs. + * However, it defers our decision of what kind of lock to use + * to the run time and delegates all locking operations to the actual + * lock. Users must define a constructor in their subclass to + * initialize . + */ +class ACE_Export ACE_Adaptive_Lock : public ACE_Lock +{ +public: + /// You must also override the destructor function to match with how + /// you construct the underneath . + virtual ~ACE_Adaptive_Lock (void); + + // = Lock/unlock operations. + + virtual int remove (void); + virtual int acquire (void); + virtual int tryacquire (void); + virtual int release (void); + virtual int acquire_read (void); + virtual int acquire_write (void); + virtual int tryacquire_read (void); + virtual int tryacquire_write (void); + virtual int tryacquire_write_upgrade (void); + void dump (void) const; + +protected: + /** + * Create and initialize create the actual lcok used in the class. + * The default constructor simply set the to 0 (null). You + * must overwrite this method for this class to work. + */ + ACE_Adaptive_Lock (void); + + ACE_Lock *lock_; +}; + +/** + * @class ACE_Semaphore + * + * @brief Wrapper for Dijkstra style general semaphores. + */ +class ACE_Export ACE_Semaphore +{ +public: + // = Initialization and termination. + /// Initialize the semaphore, with initial value of "count". + ACE_Semaphore (u_int count = 1, // By default make this unlocked. + int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7fffffff); + + /// Implicitly destroy the semaphore. + ~ACE_Semaphore (void); + + /** + * Explicitly destroy the semaphore. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Block the thread until the semaphore count becomes + /// greater than 0, then decrement it. + int acquire (void); + + /** + * Block the thread until the semaphore count becomes greater than 0 + * (at which point it is decremented) or until times out (in + * which case -1 is returned and == ). Note that + * is assumed to be in "absolute" rather than "relative" time. The + * value of is updated upon return to show the actual + * (absolute) acquisition time. + * + * NOTE: Solaris threads do not support timed semaphores. + * Therefore, if you're running on Solaris you might want to + * consider using the ACE POSIX pthreads implementation instead, + * which can be enabled by compiling ACE with + * -DACE_HAS_PTHREADS, rather than -DACE_HAS_STHREADS or + * -DACE_HAS_POSIX_SEM. */ + int acquire (ACE_Time_Value &tv); + + /** + * If == 0 then call directly. Otherwise, Block + * the thread until the semaphore count becomes greater than 0 + * (at which point it is decremented) or until times out (in + * which case -1 is returned and == ). Note that + * <*tv> is assumed to be in "absolute" rather than "relative" time. + * The value of <*tv> is updated upon return to show the actual + * (absolute) acquisition time. + * + * NOTE: Solaris threads do not support timed semaphores. + * Therefore, if you're running on Solaris you might want to + * consider using the ACE POSIX pthreads implementation instead, + * which can be enabled by compiling ACE with + * -DACE_HAS_PTHREADS, rather than -DACE_HAS_STHREADS or + * -DACE_HAS_POSIX_SEM. */ + int acquire (ACE_Time_Value *tv); + + /** + * Conditionally decrement the semaphore if count is greater than 0 + * (i.e., won't block). Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire (void); + + /// Increment the semaphore by 1, potentially unblocking a waiting + /// thread. + int release (void); + + /// Increment the semaphore by , potentially + /// unblocking waiting threads. + int release (size_t release_count); + + /** + * Acquire semaphore ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire semaphore ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire semaphore (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire semaphore (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the semaphore using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + /// Return the underlying lock. + const ACE_sema_t &lock (void) const; + +protected: + ACE_sema_t semaphore_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Semaphore &); + ACE_Semaphore (const ACE_Semaphore &); +}; + +/** + * @class ACE_Null_Semaphore + * + * @brief Implement a do nothing , i.e., all the methods are + * no ops. + * + * Although the methods are no-ops, the return values are different for + * the blocking as opposed to timed acquires. The blocking version of + * acquire() is often used to serialize access to a critical section, + * whereas the timed version is often used to wait for another thread + * to update some condition or change some shared state. When using an + * ACE_Null_Semaphore, however, there's no other thread involved to + * change a state or condition (otherwise, a null semaphore would be + * inappropriate). Returning an error value signifies that the + * state or condition has not been (and can't be) changed, which is + * consistent with the behavior of the threaded case where a timeout + * occurs before the state or condition is changed. + */ +class ACE_Export ACE_Null_Semaphore +{ +public: + ACE_Null_Semaphore (u_int count = 1, // By default make this unlocked. + int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7fffffff); + ~ACE_Null_Semaphore (void); + /// Return 0. + int remove (void); + + /// Return 0. + int acquire (void); + + /// Return -1 with == . + int acquire (ACE_Time_Value &); + + /// Return -1 with == . + int acquire (ACE_Time_Value *); + + /// Return 0. + int tryacquire (void); + + /// Return 0. + int release (void); + + /// Return 0. + int release (size_t); + + /// Return 0. + int acquire_write (void); + + /// Return 0. + int tryacquire_write (void); + + /// Return 0. + int tryacquire_write_upgrade (void); + + /// Return 0. + int acquire_read (void); + + /// Return 0. + int tryacquire_read (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_RW_Mutex + * + * @brief Wrapper for readers/writer locks. + * + * These are most useful for applications that have many more + * parallel readers than writers... + */ +class ACE_Export ACE_RW_Mutex +{ +public: + /// Initialize a readers/writer lock. + ACE_RW_Mutex (int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Implicitly destroy a readers/writer lock + ~ACE_RW_Mutex (void); + + /** + * Explicitly destroy a readers/writer lock. Note that only one + * thread should call this method since it doesn't protect against + * race conditions. + */ + int remove (void); + + /// Acquire a read lock, but block if a writer hold the lock. + int acquire_read (void); + + /// Acquire a write lock, but block if any readers or a + /// writer hold the lock. + int acquire_write (void); + + /** + * Conditionally acquire a read lock (i.e., won't block). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire_read (void); + + /// Conditionally acquire a write lock (i.e., won't block). + int tryacquire_write (void); + + /** + * Conditionally upgrade a read lock to a write lock. This only + * works if there are no other readers present, in which case the + * method returns 0. Otherwise, the method returns -1 and sets + * to . Note that the caller of this method *must* + * already possess this lock as a read lock (but this condition is + * not checked by the current implementation). + */ + int tryacquire_write_upgrade (void); + + /** + * Note, for interface uniformity with other synchronization + * wrappers we include the method. This is implemented as + * a write-lock to safe... + */ + int acquire (void); + + /** + * Note, for interface uniformity with other synchronization + * wrappers we include the method. This is implemented + * as a write-lock to be safe... Returns -1 on failure. If we + * "failed" because someone else already had the lock, is + * set to . + */ + int tryacquire (void); + + /// Unlock a readers/writer lock. + int release (void); + + /// Return the underlying lock. + const ACE_rwlock_t &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Readers/writer lock. + ACE_rwlock_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_RW_Mutex &); + ACE_RW_Mutex (const ACE_RW_Mutex &); +}; + +/** + * @class ACE_Mutex + * + * @brief wrapper (valid in same process or across + * processes (depending on TYPE flag)). + */ +class ACE_Export ACE_Mutex +{ +public: + /// Initialize the mutex. + ACE_Mutex (int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + ACE_mutexattr_t *arg = 0); + + /// Implicitly destroy the mutex. + ~ACE_Mutex (void); + + /** + * Explicitly destroy the mutex. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire (void); + + /** + * Block the thread until the mutex is acquired or times out, + * in which case -1 is returned and == . Note that + * is assumed to be in "absolute" rather than "relative" time. + * The value of is updated upon return to show the actual + * (absolute) acquisition time. + */ + int acquire (ACE_Time_Value &tv); + + /** + * If == 0 then call directly. Otherwise, block + * the thread until the mutex is acquired or times out, in + * which case -1 is returned and == . Note that + * <*tv> is assumed to be in "absolute" rather than "relative" time. + * The value of <*tv> is updated upon return to show the actual + * (absolute) acquisition time. */ + int acquire (ACE_Time_Value *tv); + + /** + * Conditionally acquire lock (i.e., don't wait on queue). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire (void); + + /// Release lock and unblock a thread at head of queue. + int release (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the interface + * consistent with the other synchronization APIs. Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the interface + * consistent with the other synchronization APIs. Returns -1 on + * failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here for consistency with the other synchronization + * APIs and usability with Lock adapters. Assumes the caller already has + * acquired the mutex and returns 0 in all cases. + */ + int tryacquire_write_upgrade (void); + + /// Return the underlying mutex. + const ACE_mutex_t &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + // = This should be protected but some C++ compilers complain... +public: +#if defined (CHORUS) || defined(ACE_HAS_PTHREADS) || defined(ACE_HAS_STHREADS) + /// This lock resides in shared memory. + ACE_mutex_t *process_lock_; + + /** + * Remember the name of the mutex if we created it so we can unlink + * it when we go away (only the actor that initialized the memory + * can destroy it). + */ + const ACE_TCHAR *lockname_; +#endif /* CHORUS || ACE_HAS_PTHREADS */ + + /// Mutex type supported by the OS. + ACE_mutex_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Mutex &); + ACE_Mutex (const ACE_Mutex &); +}; + +/** + * @class ACE_Null_Barrier + * + * @brief Implements "NULL barrier synchronization". + */ +class ACE_Export ACE_Null_Barrier +{ +public: + /// Initialize the barrier to synchronize threads. + ACE_Null_Barrier (u_int, + const char * = 0, + void * = 0); + + /// Default dtor. + ~ACE_Null_Barrier (void); + + /// Block the caller until all threads have called and + /// then allow all the caller threads to continue in parallel. + int wait (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Null_Barrier &); + ACE_Null_Barrier (const ACE_Null_Barrier &); +}; + +/** + * @class ACE_Null_Mutex + * + * @brief Implement a do nothing , i.e., all the methods are + * no ops. + */ +class ACE_Export ACE_Null_Mutex +{ +public: + ACE_Null_Mutex (const ACE_TCHAR * = 0); + ~ACE_Null_Mutex (void); + /// Return 0. + int remove (void); + + /// Return 0. + int acquire (void); + + /// Return -1 with == . + int acquire (ACE_Time_Value &timeout); + + /// Return -1 with == . + int acquire (ACE_Time_Value *timeout); + + /// Return 0. + int tryacquire (void); + + /// Return 0. + int release (void); + + /// Return 0. + int acquire_write (void); + + /// Return 0. + int tryacquire_write (void); + + /// Return 0. + int tryacquire_write_upgrade (void); + + /// Return 0. + int acquire_read (void); + + /// Return 0. + int tryacquire_read (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +class ACE_Export ACE_Noop_Token : public ACE_Null_Mutex +{ +public: + int renew (int = 0, ACE_Time_Value * =0); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Null_Condition + * + * @brief Implement a do nothing variable wrapper, + * i.e., all methods are no ops. This class is necessary since + * some C++ compilers are *very* lame... + */ +class ACE_Export ACE_Null_Condition +{ +public: + ACE_Null_Condition (const ACE_Null_Mutex &m, + const ACE_TCHAR * = 0, + void * = 0); + ~ACE_Null_Condition (void); + + /// Returns 0. + int remove (void); + + /// Returns -1 with == . + int wait (ACE_Time_Value * = 0); + + /// Returns 0. + int signal (void); + + /// Returns 0. + int broadcast (void); + ACE_Null_Mutex &mutex (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + ACE_Null_Mutex &mutex_; // Reference to mutex lock. + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Null_Condition &); + ACE_Null_Condition (const ACE_Null_Condition &); +}; + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +/** + * @class ACE_Null_Mutex_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * an ACE_Null_Mutex. + * + * This class is obsolete and should be replaced by + * ACE_Guard. + */ +class ACE_Export ACE_Null_Mutex_Guard +{ +public: + ACE_Null_Mutex_Guard (ACE_Null_Mutex &); + ~ACE_Null_Mutex_Guard (void); + int remove (void); + int locked (void); + int acquire (void); + int tryacquire (void); + int release (void); + void dump (void) const; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Null_Mutex_Guard &); + ACE_Null_Mutex_Guard (const ACE_Null_Mutex_Guard &); +}; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +/** + * @class ACE_TSS_Adapter + * + * @brief This class encapsulates a TSS object and its associated + * C++ destructor function. It is used by the ACE_TSS... + * methods (in Synch_T.cpp) in order to allow an extern + * "C" cleanup routine to be used. Needed by the "frigging" + * MVS C++ compiler. + * + * Objects of this class are stored in thread specific + * storage. ts_obj_ points to the "real" object and + * func_ is a pointer to the C++ cleanup function for ts_obj_. + */ +class ACE_Export ACE_TSS_Adapter +{ +public: + /// Initialize the adapter. + ACE_TSS_Adapter (void *object, ACE_THR_DEST f); + + /// Default dtor. + ~ACE_TSS_Adapter (void); + + /// Perform the cleanup operation. + void cleanup (void); + +//private: + + /// The real TS object. + void *ts_obj_; + + /// The real cleanup routine for ts_obj; + ACE_THR_DEST func_; +}; + +/** + * @class ACE_Event + * + * @brief A wrapper around the Win32 event locking mechanism. + * + * Portable implementation of an Event mechanism, which is + * native to Win32, but must be emulated on UNIX. Note that + * this only provides global naming and system-scope locking support + * on Win32 platforms. + */ +class ACE_Export ACE_Event +{ +public: + /// Constructor that creates event. + ACE_Event (int manual_reset = 0, + int initial_state = 0, + int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Implicitly destroy the event variable. + ~ACE_Event (void); + + /** + * Explicitly destroy the event variable. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Underlying handle to event. + ACE_event_t handle (void) const; + + /** + * Set the underlying handle to event. Note that this method assumes + * ownership of the and will close it down in . If + * you want the to stay open when is called make + * sure to call on the before closing it. You are + * responsible for the closing the existing before + * overwriting it. + */ + void handle (ACE_event_t new_handle); + + /** + * if MANUAL reset + * sleep till the event becomes signaled + * event remains signaled after wait() completes. + * else AUTO reset + * sleep till the event becomes signaled + * event resets wait() completes. + */ + int wait (void); + + /// Same as wait() above, but this one can be timed + /// is absolute time-of-day if if + /// is non-0, else it is relative time. + int wait (const ACE_Time_Value *abstime, + int use_absolute_time = 1); + + /** + * if MANUAL reset + * wake up all waiting threads + * set to signaled state + * else AUTO reset + * if no thread is waiting, set to signaled state + * if thread(s) are waiting, wake up one waiting thread and + * reset event + */ + int signal (void); + + /** + * if MANUAL reset + * wakeup all waiting threads and + * reset event + * else AUTO reset + * wakeup one waiting thread (if present) and + * reset event + */ + int pulse (void); + + /// Set to nonsignaled state. + int reset (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// The underlying handle. + ACE_event_t handle_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent copying. + ACE_Event (const ACE_Event& event); + const ACE_Event &operator= (const ACE_Event &rhs); +}; + +/** + * @class ACE_Manual_Event + * + * @brief Manual Events. + * + * Specialization of Event mechanism which wakes up all waiting + * threads on . Note that this only provides + * global naming and system-scope locking support on Win32 platforms. + */ +class ACE_Export ACE_Manual_Event : public ACE_Event +{ +public: + /// constructor which will create manual event + ACE_Manual_Event (int initial_state = 0, + int type = USYNC_THREAD, + const char *name = 0, + void *arg = 0); + +#if defined (ACE_HAS_WCHAR) + /// constructor which will create manual event (wchar_t version) + ACE_Manual_Event (int initial_state, + int type, + const wchar_t *name, + void *arg = 0); +#endif /* ACE_HAS_WCHAR */ + + /// Default dtor. + ~ACE_Manual_Event (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Auto_Event + * + * @brief Auto Events. + * + * Specialization of Event mechanism which wakes up one waiting + * thread on . Note that this only provides + * global naming and system-scope locking support on Win32 platforms. + */ +class ACE_Export ACE_Auto_Event : public ACE_Event +{ +public: + /// constructor which will create auto event + ACE_Auto_Event (int initial_state = 0, + int type = USYNC_THREAD, + const char *name = 0, + void *arg = 0); + +#if defined (ACE_HAS_WCHAR) + /// constructor which will create auto event (wchar_t version) + ACE_Auto_Event (int initial_state, + int type, + const wchar_t *name, + void *arg = 0); +#endif /* ACE_HAS_WCHAR */ + + /// Default dtor. + ~ACE_Auto_Event (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks + ACE_ALLOC_HOOK_DECLARE; +}; + +// ACE platform supports some form of threading. +#if !defined (ACE_HAS_THREADS) +/** + * @class ACE_Barrier + * + * @brief This is a no-op to make ACE "syntactically consistent." + */ +class ACE_Barrier +{ +public: + ACE_Barrier (u_int, const ACE_TCHAR * = 0, void * = 0) {} + ~ACE_Barrier (void) {} + int wait (void) { ACE_NOTSUP_RETURN (-1); } + void dump (void) const {} +}; +#else + /** + * @class ACE_Thread_Mutex + * + * @brief ACE_Thread_Mutex wrapper (only valid for threads in the same + * process). + * + * This implementation is optimized for locking threads that are + * in the same process. It maps to s on NT + * and with set to on UNIX. + * ACE_Thread_Mutex is recursive on some platforms (like + * Win32). However, on most platforms (like Solaris) it is not + * recursive. To be totally safe and portable, developers + * should use when they need a + * recursive mutex. + */ +class ACE_Export ACE_Thread_Mutex +{ + friend class ACE_Condition_Thread_Mutex; +public: + /// Constructor. + ACE_Thread_Mutex (const ACE_TCHAR *name = 0, + ACE_mutexattr_t *attributes = 0); + + /// Implicitly destroy the mutex. + ~ACE_Thread_Mutex (void); + + /** + * Explicitly destroy the mutex. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Acquire lock ownership (wait on queue if necessary). + int acquire (void); + + /** + * Block the thread until we acquire the mutex or until times + * out, in which case -1 is returned with == . Note + * that is assumed to be in "absolute" rather than "relative" + * time. The value of is updated upon return to show the + * actual (absolute) acquisition time. + */ + int acquire (ACE_Time_Value &tv); + + /** + * If == 0 the call directly. Otherwise, Block the + * thread until we acquire the mutex or until times out, in + * which case -1 is returned with == . Note that + * <*tv> is assumed to be in "absolute" rather than "relative" time. + * The value of <*tv> is updated upon return to show the actual + * (absolute) acquisition time. + */ + int acquire (ACE_Time_Value *tv); + + /** + * Conditionally acquire lock (i.e., don't wait on queue). Returns + * -1 on failure. If we "failed" because someone else already had + * the lock, is set to . + */ + int tryacquire (void); + + /// Release lock and unblock a thread at head of queue. + int release (void); + + /** + * Acquire mutex ownership. This calls and is only here + * to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire mutex ownership. This calls and is only here + * to make the interface consistent with the + * other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other synchronization APIs. + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the mutex using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + + /// Return the underlying mutex. + const ACE_thread_mutex_t &lock (void) const; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + // protected: + /// Mutex type that supports single-process locking efficiently. + ACE_thread_mutex_t lock_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Thread_Mutex &); + ACE_Thread_Mutex (const ACE_Thread_Mutex &); +}; + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +/** + * @class ACE_Thread_Mutex_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * an . + * + * This class is obsolete and should be replaced by + * ACE_Guard. + */ +class ACE_Export ACE_Thread_Mutex_Guard +{ +public: + /// Implicitly and automatically acquire the lock. + ACE_Thread_Mutex_Guard (ACE_Thread_Mutex &m, int block = 1); + + /// Implicitly release the lock. + ~ACE_Thread_Mutex_Guard (void); + + /// 1 if locked, 0 if couldn't acquire the lock (errno will contain + /// the reason for this). + int locked (void); + + /** + * Explicitly release the lock. Note that only one thread should + * call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /// Explicitly acquire the lock. + int acquire (void); + + /** + * Conditionally acquire the lock (i.e., won't block). Returns -1 + * on failure. If we "failed" because someone else already had the + * lock, is set to . + */ + int tryacquire (void); + + /// Explicitly release the lock. + int release (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Reference to the mutex. + ACE_Thread_Mutex &lock_; + + /// Keeps track of whether we acquired the lock or failed. + int owner_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Thread_Mutex_Guard &); + ACE_Thread_Mutex_Guard (const ACE_Thread_Mutex_Guard &); +}; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +class ACE_Export ACE_Condition_Attributes +{ +public: + /// Constructor + ACE_Condition_Attributes (int type = ACE_DEFAULT_SYNCH_TYPE); + + /// Destructor + ~ACE_Condition_Attributes (void); + +private: + friend class ACE_Condition_Thread_Mutex; + + /// The attributes + ACE_condattr_t attributes_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Condition_Attributes &); + ACE_Condition_Attributes (const ACE_Condition_Attributes &); +}; + +/** + * @class ACE_Condition_Thread_Mutex + * + * @brief ACE_Condition variable wrapper written using ACE_Mutexes This + * allows threads to block until shared data changes state. + * A condition variable enables threads to atomically block and + * test the condition under the protection of a mutual exclu- + * sion lock (mutex) until the condition is satisfied. That is, + * the mutex must have been held by the thread before calling + * wait or signal on the condition. If the condition is false, + * a thread blocks on a condition variable and atomically + * releases the mutex that is waiting for the condition to + * change. If another thread changes the condition, it may wake + * up waiting threads by signaling the associated condition + * variable. The waiting threads, upon awakening, reacquire the + * mutex and re-evaluate the condition. + * + * This should be an instantiation of ACE_Condition but problems + * with compilers precludes this... + */ +class ACE_Export ACE_Condition_Thread_Mutex +{ +public: + /// Initialize the condition variable. + ACE_Condition_Thread_Mutex (const ACE_Thread_Mutex &m, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Initialize the condition variable. + ACE_Condition_Thread_Mutex (const ACE_Thread_Mutex &m, + ACE_Condition_Attributes &attributes, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Implicitly destroy the condition variable. + ~ACE_Condition_Thread_Mutex (void); + + /** + * Explicitly destroy the condition variable. Note that only one + * thread should call this method since it doesn't protect against + * race conditions. + */ + int remove (void); + + /** + * Block on condition, or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" semantics. Else, if + * != 0 and the call times out before the condition is signaled + * returns -1 and sets errno to ETIME. + */ + int wait (const ACE_Time_Value *abstime); + + /// Block on condition. + int wait (void); + + /** + * Block on condition or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" wait() semantics on the + * passed as a parameter (this is useful if you need to store the + * in shared memory). Else, if != 0 and the + * call times out before the condition is signaled returns -1 + * and sets errno to ETIME. + */ + int wait (ACE_Thread_Mutex &mutex, const ACE_Time_Value *abstime = 0); + + /// Signal one waiting thread. + int signal (void); + + /// Signal *all* waiting threads. + int broadcast (void); + + /// Returns a reference to the underlying mutex_; + ACE_Thread_Mutex &mutex (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Condition variable. + ACE_cond_t cond_; + + /// Reference to mutex lock. + ACE_Thread_Mutex &mutex_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Condition_Thread_Mutex &); + ACE_Condition_Thread_Mutex (const ACE_Condition_Thread_Mutex &); +}; + +/** + * @class ACE_Recursive_Thread_Mutex + * + * @brief Implement a C++ wrapper that allows nested acquisition and + * release of a mutex that occurs in the same thread. + */ +class ACE_Export ACE_Recursive_Thread_Mutex +{ +public: + /// Initialize a recursive mutex. + ACE_Recursive_Thread_Mutex (const ACE_TCHAR *name = 0, + ACE_mutexattr_t *arg = 0); + + /// Implicitly release a recursive mutex. + ~ACE_Recursive_Thread_Mutex (void); + + /** + * Implicitly release a recursive mutex. Note that only one thread + * should call this method since it doesn't protect against race + * conditions. + */ + int remove (void); + + /** + * Acquire a recursive mutex (will increment the nesting level and + * not deadmutex if the owner of the mutex calls this method more + * than once). + */ + int acquire (void); + + /** + * Conditionally acquire a recursive mutex (i.e., won't block). + * Returns -1 on failure. If we "failed" because someone else + * already had the lock, is set to . + */ + int tryacquire (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent + * with the other synchronization APIs. + */ + int acquire_read (void); + + /** + * Acquire mutex ownership. This calls and is only + * here to make the interface consistent + * with the other synchronization APIs. + */ + int acquire_write (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other + * synchronization APIs. Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire_read (void); + + /** + * Conditionally acquire mutex (i.e., won't block). This calls + * and is only here to make the + * interface consistent with the other + * synchronization APIs. Returns -1 on failure. If we "failed" + * because someone else already had the lock, is set to + * . + */ + int tryacquire_write (void); + + /** + * This is only here to make the + * interface consistent with the other synchronization APIs. + * Assumes the caller has already acquired the mutex using one of + * the above calls, and returns 0 (success) always. + */ + int tryacquire_write_upgrade (void); + + /** + * Releases a recursive mutex (will not release mutex until all the + * nesting level drops to 0, which means the mutex is no longer + * held). + */ + int release (void); + + /// Return the id of the thread that currently owns the mutex. + ACE_thread_t get_thread_id (void); + + /** + * Return the nesting level of the recursion. When a thread has + * acquired the mutex for the first time, the nesting level == 1. + * The nesting level is incremented every time the thread acquires + * the mutex recursively. + */ + int get_nesting_level (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + // = This method should *not* be public (they hold no locks...) + void set_thread_id (ACE_thread_t t); + + /// Recursive mutex. + ACE_recursive_thread_mutex_t recursive_mutex_; + + /// Keeps track of whether has been called yet to avoid + /// multiple calls, e.g., explicitly and implicitly in the + /// destructor. This flag isn't protected by a lock, so make sure + /// that you don't have multiple threads simultaneously calling + /// on the same object, which is a bad idea anyway... + int removed_; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Recursive_Thread_Mutex &); + ACE_Recursive_Thread_Mutex (const ACE_Recursive_Thread_Mutex &); +}; + +/** + * @class ACE_RW_Thread_Mutex + * + * @brief Wrapper for readers/writer locks that exist within a process. + */ +class ACE_Export ACE_RW_Thread_Mutex : public ACE_RW_Mutex +{ +public: + ACE_RW_Thread_Mutex (const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Default dtor. + ~ACE_RW_Thread_Mutex (void); + + /** + * Conditionally upgrade a read lock to a write lock. This only + * works if there are no other readers present, in which case the + * method returns 0. Otherwise, the method returns -1 and sets + * to . Note that the caller of this method *must* + * already possess this lock as a read lock (but this condition is + * not checked by the current implementation). + */ + int tryacquire_write_upgrade (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +/** + * @class ACE_Thread_Semaphore + * + * @brief Wrapper for Dijkstra style general semaphores that work + * only within one process. + */ +class ACE_Export ACE_Thread_Semaphore : public ACE_Semaphore +{ +public: + /// Initialize the semaphore, with an initial value of , + /// maximum value of , and unlocked by default. + ACE_Thread_Semaphore (u_int count = 1, // By default make this unlocked. + const ACE_TCHAR *name = 0, + void * = 0, + int max = 0x7FFFFFFF); + + /// Default dtor. + ~ACE_Thread_Semaphore (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; + +struct ACE_Export ACE_Sub_Barrier +{ + // = Initialization. + ACE_Sub_Barrier (u_int count, + ACE_Thread_Mutex &lock, + const ACE_TCHAR *name = 0, + void *arg = 0); + + ~ACE_Sub_Barrier (void); + + ACE_Condition_Thread_Mutex barrier_finished_; + // True if this generation of the barrier is done. + + int running_threads_; + // Number of threads that are still running. + + void dump (void) const; + // Dump the state of an object. + + ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +/** + * @class ACE_Barrier + * + * @brief Implements "barrier synchronization". + * + * This class allows number of threads to synchronize + * their completion of (one round of) a task, which is known as + * "barrier synchronization". The implementation uses a + * "sub-barrier generation numbering" scheme to avoid overhead + * and to ensure that all threads wait to leave the barrier + * correct. This code is based on an article from SunOpsis + * Vol. 4, No. 1 by Richard Marejka + * (Richard.Marejka@canada.sun.com). + */ +class ACE_Export ACE_Barrier +{ +public: + /// Initialize the barrier to synchronize threads. + ACE_Barrier (u_int count, + const ACE_TCHAR *name = 0, + void *arg = 0); + + /// Default dtor. + ~ACE_Barrier (void); + + /// Block the caller until all threads have called and + /// then allow all the caller threads to continue in parallel. + int wait (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Serialize access to the barrier state. + ACE_Thread_Mutex lock_; + + /// Either 0 or 1, depending on whether we are the first generation + /// of waiters or the next generation of waiters. + int current_generation_; + + /// Total number of threads that can be waiting at any one time. + int count_; + + /** + * We keep two , one for the first "generation" of + * waiters, and one for the next "generation" of waiters. This + * efficiently solves the problem of what to do if all the first + * generation waiters don't leave the barrier before one of the + * threads calls wait() again (i.e., starts up the next generation + * barrier). + */ + ACE_Sub_Barrier sub_barrier_1_; + ACE_Sub_Barrier sub_barrier_2_; + ACE_Sub_Barrier *sub_barrier_[2]; + +private: + // = Prevent assignment and initialization. + void operator= (const ACE_Barrier &); + ACE_Barrier (const ACE_Barrier &); +}; + +#if 0 +// The following two classes are commented out since there doesn't +// appear to be a portable and robust means of implementing this +// functionality across platforms. If you know of a portable and +// robust way to implement this functionality please let us know. + +/** + * @class ACE_Process_Condition + * + * @brief ACE_Condition variable wrapper that works across processes. + */ +class ACE_Export ACE_Process_Condition +{ +public: + ACE_Process_Condition (MUTEX &m, const ACE_TCHAR *name = 0, void *arg = 0); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; +#endif /* 0 */ + +#if 0 +/** + * @class ACE_Process_Barrier + * + * @brief Implements "barrier synchronization" using ACE_Process_Mutexes! + * + * This class is just a simple wrapper for ACE_Barrier that + * selects the USYNC_PROCESS variant for the locks. + */ +class ACE_Export ACE_Process_Barrier : public ACE_Barrier +{ +public: + /// Create a Process_Barrier, passing in the optional . + ACE_Process_Barrier (u_int count, const ACE_TCHAR *name = 0); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; +#endif /* 0 */ + +/** + * @class ACE_Thread_Barrier + * + * @brief Implements "barrier synchronization" using ACE_Thread_Mutexes! + * + * This class is just a simple wrapper for ACE_Barrier that + * selects the USYNC_THREAD variant for the locks. + */ +class ACE_Export ACE_Thread_Barrier : public ACE_Barrier +{ +public: + /// Create a Thread_Barrier, passing in the optional . + ACE_Thread_Barrier (u_int count, const ACE_TCHAR *name = 0); + + /// Default dtor. + ~ACE_Thread_Barrier (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; +}; +#endif /* ACE_HAS_THREADS */ + +#if defined (__ACE_INLINE__) +#include "ace/Synch.i" +#endif /* __ACE_INLINE__ */ + +// Include the templates here. +#include "ace/Synch_T.h" + +template +class ACE_Guard; + +ACE_TEMPLATE_SPECIALIZATION +/** + * @class ACE_Guard + * + * @brief Template specialization of for the + * . + * + * This specialization is useful since it helps to speedup + * performance of the "Null_Mutex" considerably. + */ +class ACE_Export ACE_Guard +{ +public: + // = Initialization and termination methods. + ACE_Guard (ACE_Null_Mutex &) {} + ACE_Guard (ACE_Null_Mutex &, int) {} +#if defined (ACE_WIN32) + ~ACE_Guard (void) {} +#endif /* ACE_WIN32 */ + + int acquire (void) { return 0; } + int tryacquire (void) { return 0; } + int release (void) { return 0; } + int locked (void) { return 1; } + int remove (void) { return 0; } + void dump (void) const {} + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Guard &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Guard (const ACE_Guard &)) +}; + +template +class ACE_Write_Guard; + +ACE_TEMPLATE_SPECIALIZATION +/** + * @class ACE_Write_Guard + * + */ +class ACE_Export ACE_Write_Guard : public ACE_Guard +{ +public: + ACE_Write_Guard (ACE_Null_Mutex &m) + : ACE_Guard (m) {} + ACE_Write_Guard (ACE_Null_Mutex &m, int blocked) + : ACE_Guard (m, blocked) {} + + int acquire_write (void) { return 0; } + int acquire (void) { return 0; } + int tryacquire_write (void) { return 0; } + int tryacquire (void) { return 0; } + void dump (void) const {} +}; + +template +class ACE_Read_Guard; + +ACE_TEMPLATE_SPECIALIZATION +/** + * @class ACE_Read_Guard + * + */ +class ACE_Export ACE_Read_Guard : public ACE_Guard +{ +public: + ACE_Read_Guard (ACE_Null_Mutex &m) + : ACE_Guard (m) {} + ACE_Read_Guard (ACE_Null_Mutex &m, int blocked) + : ACE_Guard (m, blocked) {} + + int acquire_read (void) { return 0; } + int acquire (void) { return 0; } + int tryacquire_read (void) { return 0; } + int tryacquire (void) { return 0; } + void dump (void) const {} +}; + +#if defined (ACE_LEGACY_MODE) +# include "ace/File_Lock.h" +# include "ace/Process_Semaphore.h" +# include "ace/Process_Mutex.h" +# include "ace/RW_Process_Mutex.h" +# include "ace/Test_and_Set.h" +#endif /* ACE_LEGACY_MODE */ + +#include "ace/post.h" +#endif /* ACE_SYNCH_H */ diff --git a/ace/Threads/Synch.i b/ace/Threads/Synch.i new file mode 100644 index 00000000000..4bb95145df5 --- /dev/null +++ b/ace/Threads/Synch.i @@ -0,0 +1,965 @@ +/* -*- C++ -*- */ +// $Id$ + +ACE_INLINE +ACE_Lock::ACE_Lock (void) +{ +} + +ACE_INLINE const ACE_rwlock_t & +ACE_RW_Mutex::lock (void) const +{ +// ACE_TRACE ("ACE_RW_Mutex::lock"); + return this->lock_; +} + +ACE_INLINE int +ACE_RW_Mutex::remove (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::remove"); + int result = 0; + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::rwlock_destroy (&this->lock_); + } + return result; +} + +ACE_INLINE int +ACE_RW_Mutex::acquire_read (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::acquire_read"); + return ACE_OS::rw_rdlock (&this->lock_); +} + +ACE_INLINE int +ACE_RW_Mutex::acquire_write (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::acquire_write"); + return ACE_OS::rw_wrlock (&this->lock_); +} + +ACE_INLINE int +ACE_RW_Mutex::acquire (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::acquire"); + return ACE_OS::rw_wrlock (&this->lock_); +} + +ACE_INLINE int +ACE_RW_Mutex::tryacquire_read (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::tryacquire_read"); + return ACE_OS::rw_tryrdlock (&this->lock_); +} + +ACE_INLINE int +ACE_RW_Mutex::tryacquire_write (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::tryacquire_write"); + return ACE_OS::rw_trywrlock (&this->lock_); +} + +ACE_INLINE int +ACE_RW_Mutex::tryacquire_write_upgrade (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::tryacquire_write_upgrade"); + return ACE_OS::rw_trywrlock_upgrade (&this->lock_); +} + +ACE_INLINE int +ACE_RW_Mutex::tryacquire (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::tryacquire"); + return this->tryacquire_write (); +} + +ACE_INLINE int +ACE_RW_Mutex::release (void) +{ +// ACE_TRACE ("ACE_RW_Mutex::release"); + return ACE_OS::rw_unlock (&this->lock_); +} + +#if defined (ACE_HAS_THREADS) +ACE_INLINE int +ACE_RW_Thread_Mutex::tryacquire_write_upgrade (void) +{ +// ACE_TRACE ("ACE_RW_Thread_Mutex::tryacquire_write_upgrade"); + return ACE_OS::rw_trywrlock_upgrade (&this->lock_); +} +#endif /* ACE_HAS_THREADS */ + +ACE_INLINE int +ACE_Mutex::acquire_read (void) +{ +// ACE_TRACE ("ACE_Mutex::acquire_read"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_lock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_lock (&this->lock_); +} + +ACE_INLINE int +ACE_Mutex::acquire_write (void) +{ +// ACE_TRACE ("ACE_Mutex::acquire_write"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_lock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_lock (&this->lock_); +} + +ACE_INLINE int +ACE_Mutex::tryacquire_read (void) +{ +// ACE_TRACE ("ACE_Mutex::tryacquire_read"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_trylock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_trylock (&this->lock_); +} + +ACE_INLINE const ACE_mutex_t & +ACE_Mutex::lock (void) const +{ +// ACE_TRACE ("ACE_Mutex::lock"); +#if defined (CHORUS) + if (this->process_lock_) + return *this->process_lock_; +#endif /* CHORUS */ + return this->lock_; +} + +ACE_INLINE int +ACE_Mutex::tryacquire_write (void) +{ +// ACE_TRACE ("ACE_Mutex::tryacquire_write"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_trylock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_trylock (&this->lock_); +} + +ACE_INLINE int +ACE_Mutex::tryacquire_write_upgrade (void) +{ +// ACE_TRACE ("ACE_Mutex::tryacquire_write_upgrade"); + return 0; +} + +ACE_INLINE int +ACE_Mutex::acquire (void) +{ +// ACE_TRACE ("ACE_Mutex::acquire"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_lock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_lock (&this->lock_); +} + +ACE_INLINE int +ACE_Mutex::acquire (ACE_Time_Value &tv) +{ + // ACE_TRACE ("ACE_Mutex::acquire"); + return ACE_OS::mutex_lock (&this->lock_, tv); +} + +ACE_INLINE int +ACE_Mutex::acquire (ACE_Time_Value *tv) +{ + // ACE_TRACE ("ACE_Mutex::acquire"); + return ACE_OS::mutex_lock (&this->lock_, tv); +} + +ACE_INLINE int +ACE_Mutex::tryacquire (void) +{ +// ACE_TRACE ("ACE_Mutex::tryacquire"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_trylock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_trylock (&this->lock_); +} + +ACE_INLINE int +ACE_Mutex::release (void) +{ +// ACE_TRACE ("ACE_Mutex::release"); +#if defined (CHORUS) + if (this->process_lock_) + return ACE_OS::mutex_unlock (this->process_lock_); +#endif /* CHORUS */ + return ACE_OS::mutex_unlock (&this->lock_); +} + +ACE_INLINE int +ACE_Mutex::remove (void) +{ +// ACE_TRACE ("ACE_Mutex::remove"); +#if defined (CHORUS) || defined (ACE_HAS_PTHREADS) || defined (ACE_HAS_STHREADS) + int result = 0; + // In the case of a interprocess mutex, the owner is the first + // process that created the shared memory object. In this case, the + // lockname_ pointer will be non-zero (points to allocated memory + // for the name). Owner or not, the memory needs to be unmapped + // from the process. If we are the owner, the file used for + // shm_open needs to be deleted as well. + if (this->process_lock_) + { + if (this->removed_ == 0) + { + this->removed_ = 1; + + // Only destroy the lock if we're the ones who initialized + // it. + if (!this->lockname_) + ACE_OS::munmap ((void *) this->process_lock_, + sizeof (ACE_mutex_t)); + else + { + result = ACE_OS::mutex_destroy (this->process_lock_); + ACE_OS::munmap ((void *) this->process_lock_, + sizeof (ACE_mutex_t)); + ACE_OS::shm_unlink (this->lockname_); + ACE_OS::free (ACE_static_cast (void *, + ACE_const_cast (ACE_TCHAR *, + this->lockname_))); + } + } + } + return result; +#else /* !CHORUS */ + int result = 0; + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::mutex_destroy (&this->lock_); + } + return result; +#endif /* CHORUS */ +} + +ACE_INLINE const ACE_sema_t & +ACE_Semaphore::lock (void) const +{ +// ACE_TRACE ("ACE_Semaphore::lock"); + return this->semaphore_; +} + +ACE_INLINE int +ACE_Semaphore::remove (void) +{ +// ACE_TRACE ("ACE_Semaphore::remove"); + int result = 0; + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::sema_destroy (&this->semaphore_); + } + return result; +} + +ACE_INLINE int +ACE_Semaphore::acquire (void) +{ +// ACE_TRACE ("ACE_Semaphore::acquire"); + return ACE_OS::sema_wait (&this->semaphore_); +} + +ACE_INLINE int +ACE_Semaphore::acquire (ACE_Time_Value &tv) +{ +// ACE_TRACE ("ACE_Semaphore::acquire"); + return ACE_OS::sema_wait (&this->semaphore_, tv); +} + +ACE_INLINE int +ACE_Semaphore::acquire (ACE_Time_Value *tv) +{ +// ACE_TRACE ("ACE_Semaphore::acquire"); + return ACE_OS::sema_wait (&this->semaphore_, tv); +} + +ACE_INLINE int +ACE_Semaphore::tryacquire (void) +{ +// ACE_TRACE ("ACE_Semaphore::tryacquire"); + return ACE_OS::sema_trywait (&this->semaphore_); +} + +ACE_INLINE int +ACE_Semaphore::release (void) +{ +// ACE_TRACE ("ACE_Semaphore::release"); + return ACE_OS::sema_post (&this->semaphore_); +} + +ACE_INLINE int +ACE_Semaphore::release (size_t release_count) +{ +// ACE_TRACE ("ACE_Semaphore::release"); + return ACE_OS::sema_post (&this->semaphore_, release_count); +} + +// Acquire semaphore ownership. This calls and is only +// here to make the interface consistent with the +// other synchronization APIs. + +ACE_INLINE int +ACE_Semaphore::acquire_read (void) +{ + return this->acquire (); +} + +// Acquire semaphore ownership. This calls and is only +// here to make the interface consistent with the +// other synchronization APIs. + +ACE_INLINE int +ACE_Semaphore::acquire_write (void) +{ + return this->acquire (); +} + +// Conditionally acquire semaphore (i.e., won't block). This calls +// and is only here to make the +// interface consistent with the other synchronization APIs. + +ACE_INLINE int +ACE_Semaphore::tryacquire_read (void) +{ + return this->tryacquire (); +} + +// Conditionally acquire semaphore (i.e., won't block). This calls +// and is only here to make the +// interface consistent with the other synchronization APIs. + +ACE_INLINE int +ACE_Semaphore::tryacquire_write (void) +{ + return this->tryacquire (); +} + +// This is only here to make the interface consistent +// with the other synchronization APIs. Assumes the caller has +// already acquired the semaphore using one of the above calls, and +// returns 0 (success) always. +ACE_INLINE int +ACE_Semaphore::tryacquire_write_upgrade (void) +{ + return 0; +} + +// Null ACE_Semaphore implementation + +ACE_INLINE +ACE_Null_Semaphore::ACE_Null_Semaphore (u_int, + int, + const ACE_TCHAR *, + void *, + int) +{ +} + +ACE_INLINE +ACE_Null_Semaphore::~ACE_Null_Semaphore (void) +{ +} + +ACE_INLINE int +ACE_Null_Semaphore::remove (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::acquire (ACE_Time_Value &) +{ + errno = ETIME; + return -1; +} + +ACE_INLINE int +ACE_Null_Semaphore::acquire (ACE_Time_Value *) +{ + errno = ETIME; + return -1; +} + +ACE_INLINE int +ACE_Null_Semaphore::acquire (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::tryacquire (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::release (size_t) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::release (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::acquire_write (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::tryacquire_write (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::tryacquire_write_upgrade (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::acquire_read (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Semaphore::tryacquire_read (void) +{ + return 0; +} + +ACE_INLINE void +ACE_Null_Semaphore::dump (void) const +{ +} + +#if defined (ACE_HAS_THREADS) + +ACE_INLINE const ACE_thread_mutex_t & +ACE_Thread_Mutex::lock (void) const +{ +// ACE_TRACE ("ACE_Thread_Mutex::lock"); + return this->lock_; +} + +ACE_INLINE int +ACE_Thread_Mutex::acquire_read (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::acquire_read"); + return ACE_OS::thread_mutex_lock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::acquire_write (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::acquire_write"); + return ACE_OS::thread_mutex_lock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::tryacquire_read (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::tryacquire_read"); + return ACE_OS::thread_mutex_trylock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::tryacquire_write (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::tryacquire_write"); + return ACE_OS::thread_mutex_trylock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::tryacquire_write_upgrade (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::tryacquire_write_upgrade"); + return 0; +} + +ACE_INLINE int +ACE_Thread_Mutex::acquire (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::acquire"); + return ACE_OS::thread_mutex_lock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::acquire (ACE_Time_Value &tv) +{ + // ACE_TRACE ("ACE_Thread_Mutex::acquire"); + return ACE_OS::thread_mutex_lock (&this->lock_, tv); +} + +ACE_INLINE int +ACE_Thread_Mutex::acquire (ACE_Time_Value *tv) +{ + // ACE_TRACE ("ACE_Thread_Mutex::acquire"); + return ACE_OS::thread_mutex_lock (&this->lock_, tv); +} + +ACE_INLINE int +ACE_Thread_Mutex::tryacquire (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::tryacquire"); + return ACE_OS::thread_mutex_trylock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::release (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::release"); + return ACE_OS::thread_mutex_unlock (&this->lock_); +} + +ACE_INLINE int +ACE_Thread_Mutex::remove (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex::remove"); + int result = 0; + if (this->removed_ == 0) + { + this->removed_ = 1; + result = ACE_OS::thread_mutex_destroy (&this->lock_); + } + return result; +} + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +ACE_INLINE int +ACE_Thread_Mutex_Guard::locked (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::locked"); + return this->owner_ != -1; +} + +// Explicitly acquire the lock. + +ACE_INLINE int +ACE_Thread_Mutex_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::acquire"); + return this->owner_ = this->lock_.acquire (); +} + +// Conditionally acquire the lock (i.e., won't block). + +ACE_INLINE int +ACE_Thread_Mutex_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::tryacquire"); + return this->owner_ = this->lock_.tryacquire (); +} + +// Implicitly and automatically acquire the lock. + +ACE_INLINE +ACE_Thread_Mutex_Guard::ACE_Thread_Mutex_Guard (ACE_Thread_Mutex &m, + int block) + : lock_ (m) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::ACE_Thread_Mutex_Guard"); + if (block) + this->acquire (); + else + this->tryacquire (); +} + +// Explicitly release the lock. + +ACE_INLINE int +ACE_Thread_Mutex_Guard::release (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::release"); + if (this->owner_ != -1) + { + this->owner_ = -1; + return this->lock_.release (); + } + else + return 0; +} + +// Implicitly release the lock. + +ACE_INLINE +ACE_Thread_Mutex_Guard::~ACE_Thread_Mutex_Guard (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::~ACE_Thread_Mutex_Guard"); + this->release (); +} + +// Explicitly release the lock. + +ACE_INLINE int +ACE_Thread_Mutex_Guard::remove (void) +{ +// ACE_TRACE ("ACE_Thread_Mutex_Guard::remove"); + this->owner_ = -1; + return this->release (); +} +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +ACE_INLINE +ACE_Condition_Attributes::ACE_Condition_Attributes (int type) +{ + (void) ACE_OS::condattr_init (this->attributes_, type); +} + +ACE_INLINE +ACE_Condition_Attributes::~ACE_Condition_Attributes (void) +{ + ACE_OS::condattr_destroy (this->attributes_); +} + +ACE_INLINE int +ACE_Condition_Thread_Mutex::remove (void) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::remove"); + + // is called in a loop if the condition variable is + // BUSY. This avoids a condition where a condition is signaled and + // because of some timing problem, the thread that is to be signaled + // has called the cond_wait routine after the signal call. Since + // the condition signal is not queued in any way, deadlock occurs. + + int result = 0; + + if (this->removed_ == 0) + { + this->removed_ = 1; + + while ((result = ACE_OS::cond_destroy (&this->cond_)) == -1 + && errno == EBUSY) + { + ACE_OS::cond_broadcast (&this->cond_); + ACE_OS::thr_yield (); + } + } + return result; +} + +ACE_INLINE ACE_Thread_Mutex & +ACE_Condition_Thread_Mutex::mutex (void) +{ +// ACE_TRACE ("ACE_Condition_Thread_Mutex::mutex"); + return this->mutex_; +} + +ACE_INLINE void +ACE_Recursive_Thread_Mutex::set_thread_id (ACE_thread_t t) +{ +// ACE_TRACE ("ACE_Recursive_Thread_Mutex::set_thread_id"); +#if defined (ACE_HAS_RECURSIVE_MUTEXES) + ACE_UNUSED_ARG (t); +#else /* ! ACE_HAS_RECURSIVE_MUTEXES */ + this->recursive_mutex_.owner_id_ = t; +#endif /* ! ACE_HAS_RECURSIVE_MUTEXES */ +} + +ACE_INLINE int +ACE_Recursive_Thread_Mutex::acquire_read (void) +{ + return this->acquire (); +} + +ACE_INLINE int +ACE_Recursive_Thread_Mutex::acquire_write (void) +{ + return this->acquire (); +} + +ACE_INLINE int +ACE_Recursive_Thread_Mutex::tryacquire_read (void) +{ + return this->tryacquire (); +} + +ACE_INLINE int +ACE_Recursive_Thread_Mutex::tryacquire_write (void) +{ + return this->tryacquire (); +} + +ACE_INLINE int +ACE_Recursive_Thread_Mutex::tryacquire_write_upgrade (void) +{ + return 0; +} + +#endif /* ACE_HAS_THREADS */ + +ACE_INLINE +ACE_Null_Barrier::ACE_Null_Barrier (u_int, + const char *, + void *) +{ +} + +ACE_INLINE +ACE_Null_Barrier::~ACE_Null_Barrier (void) +{ +} + +ACE_INLINE int +ACE_Null_Barrier::wait (void) +{ + return 0; +} + +ACE_INLINE void +ACE_Null_Barrier::dump (void) const +{ +} + +ACE_INLINE +ACE_Null_Mutex::ACE_Null_Mutex (const ACE_TCHAR *) +{ +} + +ACE_INLINE +ACE_Null_Mutex::~ACE_Null_Mutex (void) +{ +} + +ACE_INLINE int +ACE_Null_Mutex::remove (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::acquire (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::acquire (ACE_Time_Value &) +{ + errno = ETIME; + return -1; +} + +ACE_INLINE int +ACE_Null_Mutex::acquire (ACE_Time_Value *) +{ + errno = ETIME; + return -1; +} + +ACE_INLINE int +ACE_Null_Mutex::tryacquire (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::release (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::acquire_write (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::tryacquire_write (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::tryacquire_write_upgrade (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::acquire_read (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex::tryacquire_read (void) +{ + return 0; +} + +ACE_INLINE void +ACE_Null_Mutex::dump (void) const +{ +} + +ACE_INLINE int +ACE_Noop_Token::renew (int, ACE_Time_Value *) +{ + return 0; +} + +ACE_INLINE void +ACE_Noop_Token::dump (void) const +{ +} + + +ACE_INLINE +ACE_Null_Condition::ACE_Null_Condition (const ACE_Null_Mutex &m, + const ACE_TCHAR *, + void*) + : mutex_ ((ACE_Null_Mutex &) m) +{ +} + +ACE_INLINE ACE_Null_Condition::~ACE_Null_Condition (void) +{ +} + +ACE_INLINE int ACE_Null_Condition::remove (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Condition::wait (ACE_Time_Value *) +{ + errno = ETIME; + return -1; +} + +ACE_INLINE int +ACE_Null_Condition::signal (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Condition::broadcast (void) +{ + return 0; +} + +ACE_INLINE ACE_Null_Mutex & +ACE_Null_Condition::mutex (void) +{ + return this->mutex_; +} + +ACE_INLINE void +ACE_Null_Condition::dump (void) const +{ +} + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +ACE_INLINE +ACE_Null_Mutex_Guard::ACE_Null_Mutex_Guard (ACE_Null_Mutex &) +{ +} + +ACE_INLINE +ACE_Null_Mutex_Guard::~ACE_Null_Mutex_Guard (void) +{ +} + +ACE_INLINE int +ACE_Null_Mutex_Guard::remove (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex_Guard::locked (void) +{ + return 1; +} + +ACE_INLINE int +ACE_Null_Mutex_Guard::acquire (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex_Guard::tryacquire (void) +{ + return 0; +} + +ACE_INLINE int +ACE_Null_Mutex_Guard::release (void) +{ + return 0; +} + +ACE_INLINE void +ACE_Null_Mutex_Guard::dump (void) const +{ +} +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +ACE_INLINE +ACE_TSS_Adapter::~ACE_TSS_Adapter (void) +{ +} + +ACE_INLINE +ACE_Manual_Event::~ACE_Manual_Event (void) +{ +} + +ACE_INLINE +ACE_Auto_Event::~ACE_Auto_Event (void) +{ +} + +#if defined (ACE_HAS_THREADS) +ACE_INLINE +ACE_RW_Thread_Mutex::~ACE_RW_Thread_Mutex (void) +{ +} + +ACE_INLINE +ACE_Thread_Semaphore::~ACE_Thread_Semaphore (void) +{ +} + +ACE_INLINE +ACE_Sub_Barrier::~ACE_Sub_Barrier (void) +{ +} + +ACE_INLINE +ACE_Barrier::~ACE_Barrier (void) +{ +} + +ACE_INLINE +ACE_Thread_Barrier::~ACE_Thread_Barrier (void) +{ +} +#endif /* ACE_HAS_THREADS */ diff --git a/ace/Threads/Synch_Options.cpp b/ace/Threads/Synch_Options.cpp new file mode 100644 index 00000000000..0febeccfb29 --- /dev/null +++ b/ace/Threads/Synch_Options.cpp @@ -0,0 +1,105 @@ +// $Id$ + +#include "ace/Synch_Options.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Synch_Options.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Synch_Options, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Synch_Options) + +void +ACE_Synch_Options::dump (void) const +{ + ACE_TRACE ("ACE_Synch_Options::dump"); +} + +// Static initialization. +// Note: these three objects require static construction and destruction. + +/* static */ +ACE_Synch_Options ACE_Synch_Options::defaults; + +/* static */ +ACE_Synch_Options ACE_Synch_Options::synch; + +/* static */ +ACE_Synch_Options ACE_Synch_Options::asynch (ACE_Synch_Options::USE_REACTOR); + +ACE_Synch_Options::ACE_Synch_Options (u_long options, + const ACE_Time_Value &timeout, + const void *arg) +{ + // ACE_TRACE ("ACE_Synch_Options::ACE_Synch_Options"); + this->set (options, timeout, arg); +} + +void +ACE_Synch_Options::set (u_long options, + const ACE_Time_Value &timeout, + const void *arg) +{ + // ACE_TRACE ("ACE_Synch_Options::set"); + this->options_ = options; + this->timeout_ = (ACE_Time_Value &) timeout; + + // Whoa, possible dependence on static initialization here. This + // function is called during initialization of the statics above. + // But, ACE_Time_Value::zero is a static object. Very fortunately, + // its bits have a value of 0. + if (this->timeout_ != ACE_Time_Value::zero) + ACE_SET_BITS (this->options_, ACE_Synch_Options::USE_TIMEOUT); + + this->arg_ = arg; +} + +int +ACE_Synch_Options::operator[] (u_long option) const +{ + ACE_TRACE ("ACE_Synch_Options::operator[]"); + return (this->options_ & option) != 0; +} + +void +ACE_Synch_Options::operator= (u_long option) +{ + ACE_TRACE ("ACE_Synch_Options::operator="); + this->options_ |= option; +} + +const ACE_Time_Value & +ACE_Synch_Options::timeout (void) const +{ + ACE_TRACE ("ACE_Synch_Options::timeout"); + return this->timeout_; +} + +void +ACE_Synch_Options::timeout (const ACE_Time_Value &tv) +{ + ACE_TRACE ("ACE_Synch_Options::timeout"); + this->timeout_ = tv; +} + +const ACE_Time_Value * +ACE_Synch_Options::time_value (void) const +{ + ACE_TRACE ("ACE_Synch_Options::time_value"); + return (*this)[USE_TIMEOUT] ? &this->timeout_ : 0; +} + +const void * +ACE_Synch_Options::arg (void) const +{ + ACE_TRACE ("ACE_Synch_Options::arg"); + return this->arg_; +} + +void +ACE_Synch_Options::arg (const void *a) +{ + ACE_TRACE ("ACE_Synch_Options::arg"); + this->arg_ = a; +} diff --git a/ace/Threads/Synch_Options.h b/ace/Threads/Synch_Options.h new file mode 100644 index 00000000000..3af324bca28 --- /dev/null +++ b/ace/Threads/Synch_Options.h @@ -0,0 +1,154 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Synch_Options.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_SYNCH_OPTIONS_H +#define ACE_SYNCH_OPTIONS_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_Synch_Options + * + * @brief Contains the values of options used to determine the + * synchronous and asynchronous behavior. + * + * Values support the following behavior (TV == "timeout" + * and UR == "use ACE_Reactor"): + * + * + * | Parameters | Description + * | + * |TV | UR | + * |-----|----------|------------------------------- + * | | + * |NULL | yes | infinite timeout (using ACE_Reactor) + * | | + * |time | yes | try asynch transaction for + * | | the specified time (using ACE_Reactor) + * | | + * |0,0 | yes | poll; try, if EWOULDBLOCK, + * | | then return immediately + * | | (using ACE_Reactor) + * | | + * |NULL | no | block forever (don't use ACE_Reactor) + * | | + * |time | no | do a blocking transaction + * | | for the specified time + * | | (don't use ACE_Reactor) + * | | + * |0,0 | no | poll; but do not initiate a + * | | nonblocking transaction + * | | (don't use ACE_Reactor) + * + */ +class ACE_Export ACE_Synch_Options +{ +public: + /// Options flags for controlling synchronization. + /** + * Note that these flags can be bit-wise "or'd" together if both + * options are desired. + */ + enum + { + /// Use the Reactor. + USE_REACTOR = 01, + /// Interprete the Time_Value. + USE_TIMEOUT = 02 + }; + + // = Initialization methods. + /// Initialize the Synch_Options based on parameters. + ACE_Synch_Options (u_long options = 0, + const ACE_Time_Value &timeout = ACE_Time_Value::zero, + const void *arg = 0); + + /// Default dtor. + ~ACE_Synch_Options (void); + + /// Initialize the Synch_Options based on parameters. + void set (u_long options = 0, + const ACE_Time_Value &timeout = ACE_Time_Value::zero, + const void *arg = 0); + + /// Get method for determining which options are enabled. + int operator[] (u_long option) const; + + /// Set method for enabling certain options. + void operator= (u_long option); + + /// Returns the "magic cookie" argument. + const void *arg (void) const; + + /// Set the "magic cookie" argument. + void arg (const void *); + + /// Returns a reference to the . This value only makes + /// sense if (*this)[USE_TIMEOUT] is true. + const ACE_Time_Value &timeout (void) const; + + /// Set the . + void timeout (const ACE_Time_Value &tv); + + /** + * Returns the address of the timeout if + * (*this)[USE_TIMEOUT] is true, else 0. This should be used with + * care, e.g., the timeout pointer should not be stored in a manner + * that will lead to dangling pointers... + */ + const ACE_Time_Value *time_value (void) const; + + // = Static data members (singletons) + + /// This is the default setting for options, which will block + /// synchronously. + static ACE_Synch_Options defaults; + + /// This is the default synchronous setting. + static ACE_Synch_Options synch; + + /// This is the default asynchronous setting. + static ACE_Synch_Options asynch; + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Keeps track of the enabled options. + u_long options_; + + /// Amount of time to wait for timeouts. + ACE_Time_Value timeout_; + + /** + * "Magic cookie" always passed in as an argument to the ACE_Reactor's + * method. Used to communicate values for + * asynchronous programming. + */ + const void *arg_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Synch_Options.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_SYNCH_OPTIONS_H */ diff --git a/ace/Threads/Synch_Options.i b/ace/Threads/Synch_Options.i new file mode 100644 index 00000000000..3c16b199ac8 --- /dev/null +++ b/ace/Threads/Synch_Options.i @@ -0,0 +1,9 @@ +/* -*- C++ -*- */ +// $Id$ + +// Synch_Options.i + +ACE_INLINE +ACE_Synch_Options::~ACE_Synch_Options (void) +{ +} diff --git a/ace/Threads/Synch_T.cpp b/ace/Threads/Synch_T.cpp new file mode 100644 index 00000000000..dccb94fc382 --- /dev/null +++ b/ace/Threads/Synch_T.cpp @@ -0,0 +1,895 @@ +// $Id$ + + +#ifndef ACE_SYNCH_T_C +#define ACE_SYNCH_T_C + +#include "ace/Threads/Thread.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Threads/Synch_T.h" +#include "ace/Logging/Log_Msg.h" + +ACE_RCSID(ace, Synch_T, "$Id$") + +#if !defined (__ACE_INLINE__) +#include "ace/Threads/Synch_T.i" +#endif /* __ACE_INLINE__ */ + + + +// This constructor isn't inlined, because SunPRO C++ 4.2 + patch +// 104631-07 has trouble compiling TAO with it inline. +template +ACE_Lock_Adapter::ACE_Lock_Adapter (void) + : lock_ (0), + delete_lock_ (1) +{ + ACE_NEW (this->lock_, + ACE_LOCKING_MECHANISM); +} + +template +ACE_Reverse_Lock::~ACE_Reverse_Lock (void) +{ +} +// **************************************************************** +// ACE_ALLOC_HOOK_DEFINE(ACE_Guard) + +template void +ACE_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Guard::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("mutex_ = %x\n"), this->lock_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("owner_ = %d\n"), this->owner_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +// ACE_ALLOC_HOOK_DEFINE(ACE_Write_Guard) + +template void +ACE_Write_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Write_Guard::dump"); + ACE_Guard::dump (); +} + +// ACE_ALLOC_HOOK_DEFINE(ACE_Read_Guard) + +template void +ACE_Read_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Read_Guard::dump"); + ACE_Guard::dump (); +} + +#if defined (ACE_HAS_THREADS) + +ACE_ALLOC_HOOK_DEFINE(ACE_Condition) + +template void +ACE_Condition::dump (void) const +{ +// ACE_TRACE ("ACE_Condition::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); +#if defined (CHORUS) + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("condname_ = %s\n"), this->condname_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("process_cond_ = %x\n"), this->process_cond_)); +#endif /* CHORUS */ + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template +ACE_Thread_Condition::ACE_Thread_Condition (MUTEX &m, + const ACE_TCHAR *name, + void *arg) + : ACE_Condition (m, USYNC_THREAD, name, arg) +{ +// ACE_TRACE ("ACE_Thread_Condition::ACE_Thread_Condition"); +} + +template void +ACE_Thread_Condition::dump (void) const +{ +// ACE_TRACE ("ACE_Thread_Condition::dump"); + + ACE_Condition::dump (); +} + +template +ACE_Condition::ACE_Condition (MUTEX &m, + int type, + const ACE_TCHAR *name, + void *arg) + : +#if defined (CHORUS) + process_cond_(0), + condname_ (0), +#endif /* CHORUS */ + mutex_ (m) +{ + +#if defined(CHORUS) + if (type == USYNC_PROCESS) + { + // Let's see if the shared memory entity already exists. + ACE_HANDLE fd = ACE_OS::shm_open (name, + O_RDWR | O_CREAT | O_EXCL, + ACE_DEFAULT_FILE_PERMS); + if (fd == ACE_INVALID_HANDLE) + { + if (errno == EEXIST) + fd = ACE_OS::shm_open (name, + O_RDWR | O_CREAT, + ACE_DEFAULT_FILE_PERMS); + else + return; + } + else + { + // We own this shared memory object! Let's set its size. + if (ACE_OS::ftruncate (fd, + sizeof (ACE_mutex_t)) == -1) + { + ACE_OS::close (fd); + return; + } + this->condname_ = ACE_OS::strdup (name); + if (this->condname_ == 0) + { + ACE_OS::close (fd); + return; + } + } + + this->process_cond_ = + (ACE_cond_t *) ACE_OS::mmap (0, + sizeof (ACE_cond_t), + PROT_RDWR, + MAP_SHARED, + fd, + 0); + ACE_OS::close (fd); + if (this->process_cond_ == MAP_FAILED) + return; + + if (this->condname_ + && ACE_OS::cond_init (this->process_cond_, + type, + name, + arg) != 0) + return; + } + // It is ok to fall through into the below if the + // USYNC_PROCESS flag is not enabled. +#endif /* CHORUS */ + + // ACE_TRACE ("ACE_Condition::ACE_Condition"); + + if (ACE_OS::cond_init (&this->cond_, + (short) type, + name, + arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Condition::ACE_Condition"))); +} + +template +ACE_Condition::~ACE_Condition (void) +{ + // ACE_TRACE ("ACE_Condition::~ACE_Condition"); + + if (this->remove () == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Condition::~ACE_Condition"))); +} + +template int +ACE_Condition::wait (void) +{ + // ACE_TRACE ("ACE_Condition::wait"); +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_wait (this->process_cond_, + &this->mutex_.lock_); +#endif /* CHORUS */ + return ACE_OS::cond_wait (&this->cond_, + &this->mutex_.lock_); +} + +template int +ACE_Condition::wait (MUTEX &mutex, + const ACE_Time_Value *abstime) +{ +// ACE_TRACE ("ACE_Condition::wait"); + if (abstime == 0) + return this->wait (); + else + { +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_timedwait (this->process_cond_, + &mutex_.lock_, + (ACE_Time_Value *) abstime); +#endif /* CHORUS */ + return ACE_OS::cond_timedwait (&this->cond_, + &mutex.lock_, + (ACE_Time_Value *) abstime); + } +} + +// Peform an "alertable" timed wait. If the argument ABSTIME == 0 +// then we do a regular cond_wait(), else we do a timed wait for up to +// ABSTIME using the Solaris cond_timedwait() function. + +template int +ACE_Condition::wait (const ACE_Time_Value *abstime) +{ +// ACE_TRACE ("ACE_Condition::wait"); + return this->wait (this->mutex_, abstime); +} +#endif /* ACE_HAS_THREADS */ + +ACE_ALLOC_HOOK_DEFINE(ACE_TSS) + +template +ACE_TSS::~ACE_TSS (void) +{ + // We can't call until *all* of the threads + // that are using that key have done an . + // Otherwise, we'll end up with "dangling TSS pointers." + ACE_OS::thr_key_detach (this); +} + +template TYPE * +ACE_TSS::operator-> () const +{ + return this->ts_get (); +} + +template +ACE_TSS::operator TYPE *(void) const +{ + return this->ts_get (); +} + +template TYPE * +ACE_TSS::make_TSS_TYPE (void) const +{ + TYPE *temp = 0; + ACE_NEW_RETURN (temp, + TYPE, + 0); + return temp; +} + +template void +ACE_TSS::dump (void) const +{ +// ACE_TRACE ("ACE_TSS::dump"); +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->keylock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("key_ = %d\n"), this->key_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nonce_ = %d"), this->once_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +#endif /* defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) */ +} + +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) +#if defined (ACE_HAS_THR_C_DEST) +extern "C" void ACE_TSS_C_cleanup(void *); // defined in Synch.cpp +#endif /* ACE_HAS_THR_C_DEST */ + +template void +ACE_TSS::cleanup (void *ptr) +{ + // Cast this to the concrete TYPE * so the destructor gets called. + delete (TYPE *) ptr; +} + +template int +ACE_TSS::ts_init (void) const +{ + // Insure that we are serialized! + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->keylock_, 0); + + // Use the Double-Check pattern to make sure we only create the key + // once! + if (this->once_ == 0) + { + if (ACE_Thread::keycreate (ACE_const_cast (ACE_thread_key_t *, &this->key_), +#if defined (ACE_HAS_THR_C_DEST) + &ACE_TSS_C_cleanup, +#else + &ACE_TSS::cleanup, +#endif /* ACE_HAS_THR_C_DEST */ + (void *) this) != 0) + return -1; // Major problems, this should *never* happen! + else + { + // This *must* come last to avoid race conditions! Note that + // we need to "cast away const..." + * ACE_const_cast (int*, &this->once_) = 1; + return 0; + } + } + + return -1; +} + +template +ACE_TSS::ACE_TSS (TYPE *ts_obj) + : once_ (0), + key_ (ACE_OS::NULL_key) +{ + // If caller has passed us a non-NULL TYPE *, then we'll just use + // this to initialize the thread-specific value. Thus, subsequent + // calls to operator->() will return this value. This is useful + // since it enables us to assign objects to thread-specific data + // that have arbitrarily complex constructors! + + if (ts_obj != 0) + { + if (this->ts_init () == -1) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + // What should we do if this call fails?! +#if defined (ACE_HAS_WINCE) + ::MessageBox (NULL, + L"ACE_Thread::keycreate() failed!", + L"ACE_TSS::ACE_TSS", + MB_OK); +#else + ACE_OS::fprintf (stderr, + "ACE_Thread::keycreate() failed!"); +#endif /* ACE_HAS_WINCE */ + return; + } + +#if defined (ACE_HAS_THR_C_DEST) + // Encapsulate a ts_obj and it's destructor in an + // ACE_TSS_Adapter. + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *) ts_obj, + ACE_TSS::cleanup)); + + // Put the adapter in thread specific storage + if (ACE_Thread::setspecific (this->key_, + (void *) tss_adapter) != 0) + { + delete tss_adapter; + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Thread::setspecific() failed!"))); + } +#else + if (ACE_Thread::setspecific (this->key_, + (void *) ts_obj) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Thread::setspecific() failed!"))); +#endif /* ACE_HAS_THR_C_DEST */ + } +} + +template TYPE * +ACE_TSS::ts_get (void) const +{ + if (this->once_ == 0) + // Create and initialize thread-specific ts_obj. + this->ts_init (); + + TYPE *ts_obj = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + + // Get the adapter from thread-specific storage + if (ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter) == -1) + return 0; // This should not happen! + + // Check to see if this is the first time in for this thread. + if (tss_adapter == 0) +#else + // Get the ts_obj from thread-specific storage. Note that no locks + // are required here... + if (ACE_Thread::getspecific (this->key_, + (void **) &ts_obj) == -1) + return 0; // This should not happen! + + // Check to see if this is the first time in for this thread. + if (ts_obj == 0) +#endif /* ACE_HAS_THR_C_DEST */ + { + // Allocate memory off the heap and store it in a pointer in + // thread-specific storage (on the stack...). + + ts_obj = this->make_TSS_TYPE (); + + if (ts_obj == 0) + return 0; + +#if defined (ACE_HAS_THR_C_DEST) + // Encapsulate a ts_obj and it's destructor in an + // ACE_TSS_Adapter. + ACE_NEW_RETURN (tss_adapter, + ACE_TSS_Adapter (ts_obj, + ACE_TSS::cleanup), 0); + + // Put the adapter in thread specific storage + if (ACE_Thread::setspecific (this->key_, + (void *) tss_adapter) != 0) + { + delete tss_adapter; + delete ts_obj; + return 0; // Major problems, this should *never* happen! + } +#else + // Store the dynamically allocated pointer in thread-specific + // storage. + if (ACE_Thread::setspecific (this->key_, + (void *) ts_obj) != 0) + { + delete ts_obj; + return 0; // Major problems, this should *never* happen! + } +#endif /* ACE_HAS_THR_C_DEST */ + } + +#if defined (ACE_HAS_THR_C_DEST) + // Return the underlying ts object. + return (TYPE *) tss_adapter->ts_obj_; +#else + return ts_obj; +#endif /* ACE_HAS_THR_C_DEST */ +} + +// Get the thread-specific object for the key associated with this +// object. Returns 0 if the ts_obj has never been initialized, +// otherwise returns a pointer to the ts_obj. + +template TYPE * +ACE_TSS::ts_object (void) const +{ + // Ensure that we are serialized! + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->keylock_, 0); + + if (this->once_ == 0) // Return 0 if we've never been initialized. + return 0; + + TYPE *ts_obj = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + + // Get the tss adapter from thread-specific storage + if (ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter) == -1) + return 0; // This should not happen! + else if (tss_adapter != 0) + // Extract the real TS object. + ts_obj = (TYPE *) tss_adapter->ts_obj_; +#else + if (ACE_Thread::getspecific (this->key_, + (void **) &ts_obj) == -1) + return 0; // This should not happen! +#endif /* ACE_HAS_THR_C_DEST */ + + return ts_obj; +} + +template TYPE * +ACE_TSS::ts_object (TYPE *new_ts_obj) +{ + // Note, we shouldn't hold the keylock at this point because + // does it for us and we'll end up with deadlock + // otherwise... + if (this->once_ == 0) + // Create and initialize thread-specific ts_obj. + this->ts_init (); + + // Ensure that we are serialized! + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->keylock_, 0); + + TYPE *ts_obj = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + + if (ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter) == -1) + return 0; // This should not happen! + + if (tss_adapter != 0) + { + ts_obj = (TYPE *) tss_adapter->ts_obj_; + delete tss_adapter; // don't need this anymore + } + + ACE_NEW_RETURN (tss_adapter, + ACE_TSS_Adapter ((void *) new_ts_obj, + ACE_TSS::cleanup), + 0); + + if (ACE_Thread::setspecific (this->key_, + (void *) tss_adapter) == -1) + { + delete tss_adapter; + return ts_obj; // This should not happen! + } +#else + if (ACE_Thread::getspecific (this->key_, + (void **) &ts_obj) == -1) + return 0; // This should not happen! + if (ACE_Thread::setspecific (this->key_, + (void *) new_ts_obj) == -1) + return ts_obj; // This should not happen! +#endif /* ACE_HAS_THR_C_DEST */ + + return ts_obj; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_TSS_Guard) + +template void +ACE_TSS_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_TSS_Guard::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("key_ = %d"), this->key_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template void +ACE_TSS_Guard::init_key (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::init_key"); + + this->key_ = ACE_OS::NULL_key; + ACE_Thread::keycreate (&this->key_, +#if defined (ACE_HAS_THR_C_DEST) + &ACE_TSS_C_cleanup, +#else + &ACE_TSS_Guard::cleanup, +#endif /* ACE_HAS_THR_C_DEST */ + (void *) this); +} + +template +ACE_TSS_Guard::ACE_TSS_Guard (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::ACE_TSS_Guard"); + this->init_key (); +} + +template int +ACE_TSS_Guard::release (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::release"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *)tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->release (); +} + +template int +ACE_TSS_Guard::remove (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::remove"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->remove (); +} + +template +ACE_TSS_Guard::~ACE_TSS_Guard (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::~ACE_TSS_Guard"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + // Make sure that this pointer is NULL when we shut down... + ACE_Thread::setspecific (this->key_, 0); + ACE_Thread::keyfree (this->key_); + // Destructor releases lock. + delete guard; +} + +template void +ACE_TSS_Guard::cleanup (void *ptr) +{ +// ACE_TRACE ("ACE_TSS_Guard::cleanup"); + + // Destructor releases lock. + delete (ACE_Guard *) ptr; +} + +template +ACE_TSS_Guard::ACE_TSS_Guard (ACE_LOCK &lock, int block) +{ +// ACE_TRACE ("ACE_TSS_Guard::ACE_TSS_Guard"); + + this->init_key (); + ACE_Guard *guard; + ACE_NEW (guard, + ACE_Guard (lock, + block)); + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *) guard, + ACE_TSS_Guard::cleanup)); + ACE_Thread::setspecific (this->key_, + (void *) tss_adapter); +#else + ACE_Thread::setspecific (this->key_, + (void *) guard); +#endif /* ACE_HAS_THR_C_DEST */ +} + +template int +ACE_TSS_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::acquire"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->acquire (); +} + +template int +ACE_TSS_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::tryacquire"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->tryacquire (); +} + +template +ACE_TSS_Write_Guard::ACE_TSS_Write_Guard (ACE_LOCK &lock, + int block) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::ACE_TSS_Write_Guard"); + + this->init_key (); + ACE_Guard *guard; + ACE_NEW (guard, + ACE_Write_Guard (lock, + block)); + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *) guard, + ACE_TSS_Guard::cleanup)); + ACE_Thread::setspecific (this->key_, + (void *) tss_adapter); +#else + ACE_Thread::setspecific (this->key_, + (void *) guard); +#endif /* ACE_HAS_THR_C_DEST */ +} + +template int +ACE_TSS_Write_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::acquire"); + + ACE_Write_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->acquire_write (); +} + +template int +ACE_TSS_Write_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::tryacquire"); + + ACE_Write_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->tryacquire_write (); +} + +template int +ACE_TSS_Write_Guard::acquire_write (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::acquire_write"); + + return this->acquire (); +} + +template int +ACE_TSS_Write_Guard::tryacquire_write (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::tryacquire_write"); + + return this->tryacquire (); +} + +template void +ACE_TSS_Write_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::dump"); + ACE_TSS_Guard::dump (); +} + +template +ACE_TSS_Read_Guard::ACE_TSS_Read_Guard (ACE_LOCK &lock, int block) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::ACE_TSS_Read_Guard"); + + this->init_key (); + ACE_Guard *guard; + ACE_NEW (guard, + ACE_Read_Guard (lock, + block)); +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *)guard, + ACE_TSS_Guard::cleanup)); + ACE_Thread::setspecific (this->key_, + (void *) tss_adapter); +#else + ACE_Thread::setspecific (this->key_, + (void *) guard); +#endif /* ACE_HAS_THR_C_DEST */ +} + +template int +ACE_TSS_Read_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::acquire"); + + ACE_Read_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->acquire_read (); +} + +template int +ACE_TSS_Read_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::tryacquire"); + + ACE_Read_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->tryacquire_read (); +} + +template int +ACE_TSS_Read_Guard::acquire_read (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::acquire_read"); + + return this->acquire (); +} + +template int +ACE_TSS_Read_Guard::tryacquire_read (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::tryacquire_read"); + + return this->tryacquire (); +} + +template void +ACE_TSS_Read_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::dump"); + ACE_TSS_Guard::dump (); +} + + +#endif /* defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) */ + +#endif /* ACE_SYNCH_T_C */ diff --git a/ace/Threads/Synch_T.cpp~ b/ace/Threads/Synch_T.cpp~ new file mode 100644 index 00000000000..2ec9d59acce --- /dev/null +++ b/ace/Threads/Synch_T.cpp~ @@ -0,0 +1,895 @@ +// $Id$ + + +#ifndef ACE_SYNCH_T_C +#define ACE_SYNCH_T_C + +#include "ace/Thread.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Synch_T.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(ace, Synch_T, "$Id$") + +#if !defined (__ACE_INLINE__) +#include "ace/Synch_T.i" +#endif /* __ACE_INLINE__ */ + + + +// This constructor isn't inlined, because SunPRO C++ 4.2 + patch +// 104631-07 has trouble compiling TAO with it inline. +template +ACE_Lock_Adapter::ACE_Lock_Adapter (void) + : lock_ (0), + delete_lock_ (1) +{ + ACE_NEW (this->lock_, + ACE_LOCKING_MECHANISM); +} + +template +ACE_Reverse_Lock::~ACE_Reverse_Lock (void) +{ +} +// **************************************************************** +// ACE_ALLOC_HOOK_DEFINE(ACE_Guard) + +template void +ACE_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Guard::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("mutex_ = %x\n"), this->lock_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("owner_ = %d\n"), this->owner_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +// ACE_ALLOC_HOOK_DEFINE(ACE_Write_Guard) + +template void +ACE_Write_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Write_Guard::dump"); + ACE_Guard::dump (); +} + +// ACE_ALLOC_HOOK_DEFINE(ACE_Read_Guard) + +template void +ACE_Read_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_Read_Guard::dump"); + ACE_Guard::dump (); +} + +#if defined (ACE_HAS_THREADS) + +ACE_ALLOC_HOOK_DEFINE(ACE_Condition) + +template void +ACE_Condition::dump (void) const +{ +// ACE_TRACE ("ACE_Condition::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); +#if defined (CHORUS) + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("condname_ = %s\n"), this->condname_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("process_cond_ = %x\n"), this->process_cond_)); +#endif /* CHORUS */ + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template +ACE_Thread_Condition::ACE_Thread_Condition (MUTEX &m, + const ACE_TCHAR *name, + void *arg) + : ACE_Condition (m, USYNC_THREAD, name, arg) +{ +// ACE_TRACE ("ACE_Thread_Condition::ACE_Thread_Condition"); +} + +template void +ACE_Thread_Condition::dump (void) const +{ +// ACE_TRACE ("ACE_Thread_Condition::dump"); + + ACE_Condition::dump (); +} + +template +ACE_Condition::ACE_Condition (MUTEX &m, + int type, + const ACE_TCHAR *name, + void *arg) + : +#if defined (CHORUS) + process_cond_(0), + condname_ (0), +#endif /* CHORUS */ + mutex_ (m) +{ + +#if defined(CHORUS) + if (type == USYNC_PROCESS) + { + // Let's see if the shared memory entity already exists. + ACE_HANDLE fd = ACE_OS::shm_open (name, + O_RDWR | O_CREAT | O_EXCL, + ACE_DEFAULT_FILE_PERMS); + if (fd == ACE_INVALID_HANDLE) + { + if (errno == EEXIST) + fd = ACE_OS::shm_open (name, + O_RDWR | O_CREAT, + ACE_DEFAULT_FILE_PERMS); + else + return; + } + else + { + // We own this shared memory object! Let's set its size. + if (ACE_OS::ftruncate (fd, + sizeof (ACE_mutex_t)) == -1) + { + ACE_OS::close (fd); + return; + } + this->condname_ = ACE_OS::strdup (name); + if (this->condname_ == 0) + { + ACE_OS::close (fd); + return; + } + } + + this->process_cond_ = + (ACE_cond_t *) ACE_OS::mmap (0, + sizeof (ACE_cond_t), + PROT_RDWR, + MAP_SHARED, + fd, + 0); + ACE_OS::close (fd); + if (this->process_cond_ == MAP_FAILED) + return; + + if (this->condname_ + && ACE_OS::cond_init (this->process_cond_, + type, + name, + arg) != 0) + return; + } + // It is ok to fall through into the below if the + // USYNC_PROCESS flag is not enabled. +#endif /* CHORUS */ + + // ACE_TRACE ("ACE_Condition::ACE_Condition"); + + if (ACE_OS::cond_init (&this->cond_, + (short) type, + name, + arg) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Condition::ACE_Condition"))); +} + +template +ACE_Condition::~ACE_Condition (void) +{ + // ACE_TRACE ("ACE_Condition::~ACE_Condition"); + + if (this->remove () == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Condition::~ACE_Condition"))); +} + +template int +ACE_Condition::wait (void) +{ + // ACE_TRACE ("ACE_Condition::wait"); +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_wait (this->process_cond_, + &this->mutex_.lock_); +#endif /* CHORUS */ + return ACE_OS::cond_wait (&this->cond_, + &this->mutex_.lock_); +} + +template int +ACE_Condition::wait (MUTEX &mutex, + const ACE_Time_Value *abstime) +{ +// ACE_TRACE ("ACE_Condition::wait"); + if (abstime == 0) + return this->wait (); + else + { +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_timedwait (this->process_cond_, + &mutex_.lock_, + (ACE_Time_Value *) abstime); +#endif /* CHORUS */ + return ACE_OS::cond_timedwait (&this->cond_, + &mutex.lock_, + (ACE_Time_Value *) abstime); + } +} + +// Peform an "alertable" timed wait. If the argument ABSTIME == 0 +// then we do a regular cond_wait(), else we do a timed wait for up to +// ABSTIME using the Solaris cond_timedwait() function. + +template int +ACE_Condition::wait (const ACE_Time_Value *abstime) +{ +// ACE_TRACE ("ACE_Condition::wait"); + return this->wait (this->mutex_, abstime); +} +#endif /* ACE_HAS_THREADS */ + +ACE_ALLOC_HOOK_DEFINE(ACE_TSS) + +template +ACE_TSS::~ACE_TSS (void) +{ + // We can't call until *all* of the threads + // that are using that key have done an . + // Otherwise, we'll end up with "dangling TSS pointers." + ACE_OS::thr_key_detach (this); +} + +template TYPE * +ACE_TSS::operator-> () const +{ + return this->ts_get (); +} + +template +ACE_TSS::operator TYPE *(void) const +{ + return this->ts_get (); +} + +template TYPE * +ACE_TSS::make_TSS_TYPE (void) const +{ + TYPE *temp = 0; + ACE_NEW_RETURN (temp, + TYPE, + 0); + return temp; +} + +template void +ACE_TSS::dump (void) const +{ +// ACE_TRACE ("ACE_TSS::dump"); +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->keylock_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("key_ = %d\n"), this->key_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nonce_ = %d"), this->once_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +#endif /* defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) */ +} + +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) +#if defined (ACE_HAS_THR_C_DEST) +extern "C" void ACE_TSS_C_cleanup(void *); // defined in Synch.cpp +#endif /* ACE_HAS_THR_C_DEST */ + +template void +ACE_TSS::cleanup (void *ptr) +{ + // Cast this to the concrete TYPE * so the destructor gets called. + delete (TYPE *) ptr; +} + +template int +ACE_TSS::ts_init (void) const +{ + // Insure that we are serialized! + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->keylock_, 0); + + // Use the Double-Check pattern to make sure we only create the key + // once! + if (this->once_ == 0) + { + if (ACE_Thread::keycreate (ACE_const_cast (ACE_thread_key_t *, &this->key_), +#if defined (ACE_HAS_THR_C_DEST) + &ACE_TSS_C_cleanup, +#else + &ACE_TSS::cleanup, +#endif /* ACE_HAS_THR_C_DEST */ + (void *) this) != 0) + return -1; // Major problems, this should *never* happen! + else + { + // This *must* come last to avoid race conditions! Note that + // we need to "cast away const..." + * ACE_const_cast (int*, &this->once_) = 1; + return 0; + } + } + + return -1; +} + +template +ACE_TSS::ACE_TSS (TYPE *ts_obj) + : once_ (0), + key_ (ACE_OS::NULL_key) +{ + // If caller has passed us a non-NULL TYPE *, then we'll just use + // this to initialize the thread-specific value. Thus, subsequent + // calls to operator->() will return this value. This is useful + // since it enables us to assign objects to thread-specific data + // that have arbitrarily complex constructors! + + if (ts_obj != 0) + { + if (this->ts_init () == -1) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + // What should we do if this call fails?! +#if defined (ACE_HAS_WINCE) + ::MessageBox (NULL, + L"ACE_Thread::keycreate() failed!", + L"ACE_TSS::ACE_TSS", + MB_OK); +#else + ACE_OS::fprintf (stderr, + "ACE_Thread::keycreate() failed!"); +#endif /* ACE_HAS_WINCE */ + return; + } + +#if defined (ACE_HAS_THR_C_DEST) + // Encapsulate a ts_obj and it's destructor in an + // ACE_TSS_Adapter. + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *) ts_obj, + ACE_TSS::cleanup)); + + // Put the adapter in thread specific storage + if (ACE_Thread::setspecific (this->key_, + (void *) tss_adapter) != 0) + { + delete tss_adapter; + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Thread::setspecific() failed!"))); + } +#else + if (ACE_Thread::setspecific (this->key_, + (void *) ts_obj) != 0) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("ACE_Thread::setspecific() failed!"))); +#endif /* ACE_HAS_THR_C_DEST */ + } +} + +template TYPE * +ACE_TSS::ts_get (void) const +{ + if (this->once_ == 0) + // Create and initialize thread-specific ts_obj. + this->ts_init (); + + TYPE *ts_obj = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + + // Get the adapter from thread-specific storage + if (ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter) == -1) + return 0; // This should not happen! + + // Check to see if this is the first time in for this thread. + if (tss_adapter == 0) +#else + // Get the ts_obj from thread-specific storage. Note that no locks + // are required here... + if (ACE_Thread::getspecific (this->key_, + (void **) &ts_obj) == -1) + return 0; // This should not happen! + + // Check to see if this is the first time in for this thread. + if (ts_obj == 0) +#endif /* ACE_HAS_THR_C_DEST */ + { + // Allocate memory off the heap and store it in a pointer in + // thread-specific storage (on the stack...). + + ts_obj = this->make_TSS_TYPE (); + + if (ts_obj == 0) + return 0; + +#if defined (ACE_HAS_THR_C_DEST) + // Encapsulate a ts_obj and it's destructor in an + // ACE_TSS_Adapter. + ACE_NEW_RETURN (tss_adapter, + ACE_TSS_Adapter (ts_obj, + ACE_TSS::cleanup), 0); + + // Put the adapter in thread specific storage + if (ACE_Thread::setspecific (this->key_, + (void *) tss_adapter) != 0) + { + delete tss_adapter; + delete ts_obj; + return 0; // Major problems, this should *never* happen! + } +#else + // Store the dynamically allocated pointer in thread-specific + // storage. + if (ACE_Thread::setspecific (this->key_, + (void *) ts_obj) != 0) + { + delete ts_obj; + return 0; // Major problems, this should *never* happen! + } +#endif /* ACE_HAS_THR_C_DEST */ + } + +#if defined (ACE_HAS_THR_C_DEST) + // Return the underlying ts object. + return (TYPE *) tss_adapter->ts_obj_; +#else + return ts_obj; +#endif /* ACE_HAS_THR_C_DEST */ +} + +// Get the thread-specific object for the key associated with this +// object. Returns 0 if the ts_obj has never been initialized, +// otherwise returns a pointer to the ts_obj. + +template TYPE * +ACE_TSS::ts_object (void) const +{ + // Ensure that we are serialized! + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, (ACE_Thread_Mutex &) this->keylock_, 0); + + if (this->once_ == 0) // Return 0 if we've never been initialized. + return 0; + + TYPE *ts_obj = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + + // Get the tss adapter from thread-specific storage + if (ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter) == -1) + return 0; // This should not happen! + else if (tss_adapter != 0) + // Extract the real TS object. + ts_obj = (TYPE *) tss_adapter->ts_obj_; +#else + if (ACE_Thread::getspecific (this->key_, + (void **) &ts_obj) == -1) + return 0; // This should not happen! +#endif /* ACE_HAS_THR_C_DEST */ + + return ts_obj; +} + +template TYPE * +ACE_TSS::ts_object (TYPE *new_ts_obj) +{ + // Note, we shouldn't hold the keylock at this point because + // does it for us and we'll end up with deadlock + // otherwise... + if (this->once_ == 0) + // Create and initialize thread-specific ts_obj. + this->ts_init (); + + // Ensure that we are serialized! + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->keylock_, 0); + + TYPE *ts_obj = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + + if (ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter) == -1) + return 0; // This should not happen! + + if (tss_adapter != 0) + { + ts_obj = (TYPE *) tss_adapter->ts_obj_; + delete tss_adapter; // don't need this anymore + } + + ACE_NEW_RETURN (tss_adapter, + ACE_TSS_Adapter ((void *) new_ts_obj, + ACE_TSS::cleanup), + 0); + + if (ACE_Thread::setspecific (this->key_, + (void *) tss_adapter) == -1) + { + delete tss_adapter; + return ts_obj; // This should not happen! + } +#else + if (ACE_Thread::getspecific (this->key_, + (void **) &ts_obj) == -1) + return 0; // This should not happen! + if (ACE_Thread::setspecific (this->key_, + (void *) new_ts_obj) == -1) + return ts_obj; // This should not happen! +#endif /* ACE_HAS_THR_C_DEST */ + + return ts_obj; +} + +ACE_ALLOC_HOOK_DEFINE(ACE_TSS_Guard) + +template void +ACE_TSS_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_TSS_Guard::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("key_ = %d"), this->key_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template void +ACE_TSS_Guard::init_key (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::init_key"); + + this->key_ = ACE_OS::NULL_key; + ACE_Thread::keycreate (&this->key_, +#if defined (ACE_HAS_THR_C_DEST) + &ACE_TSS_C_cleanup, +#else + &ACE_TSS_Guard::cleanup, +#endif /* ACE_HAS_THR_C_DEST */ + (void *) this); +} + +template +ACE_TSS_Guard::ACE_TSS_Guard (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::ACE_TSS_Guard"); + this->init_key (); +} + +template int +ACE_TSS_Guard::release (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::release"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *)tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->release (); +} + +template int +ACE_TSS_Guard::remove (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::remove"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->remove (); +} + +template +ACE_TSS_Guard::~ACE_TSS_Guard (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::~ACE_TSS_Guard"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + // Make sure that this pointer is NULL when we shut down... + ACE_Thread::setspecific (this->key_, 0); + ACE_Thread::keyfree (this->key_); + // Destructor releases lock. + delete guard; +} + +template void +ACE_TSS_Guard::cleanup (void *ptr) +{ +// ACE_TRACE ("ACE_TSS_Guard::cleanup"); + + // Destructor releases lock. + delete (ACE_Guard *) ptr; +} + +template +ACE_TSS_Guard::ACE_TSS_Guard (ACE_LOCK &lock, int block) +{ +// ACE_TRACE ("ACE_TSS_Guard::ACE_TSS_Guard"); + + this->init_key (); + ACE_Guard *guard; + ACE_NEW (guard, + ACE_Guard (lock, + block)); + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *) guard, + ACE_TSS_Guard::cleanup)); + ACE_Thread::setspecific (this->key_, + (void *) tss_adapter); +#else + ACE_Thread::setspecific (this->key_, + (void *) guard); +#endif /* ACE_HAS_THR_C_DEST */ +} + +template int +ACE_TSS_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::acquire"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->acquire (); +} + +template int +ACE_TSS_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_TSS_Guard::tryacquire"); + + ACE_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->tryacquire (); +} + +template +ACE_TSS_Write_Guard::ACE_TSS_Write_Guard (ACE_LOCK &lock, + int block) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::ACE_TSS_Write_Guard"); + + this->init_key (); + ACE_Guard *guard; + ACE_NEW (guard, + ACE_Write_Guard (lock, + block)); + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *) guard, + ACE_TSS_Guard::cleanup)); + ACE_Thread::setspecific (this->key_, + (void *) tss_adapter); +#else + ACE_Thread::setspecific (this->key_, + (void *) guard); +#endif /* ACE_HAS_THR_C_DEST */ +} + +template int +ACE_TSS_Write_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::acquire"); + + ACE_Write_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->acquire_write (); +} + +template int +ACE_TSS_Write_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::tryacquire"); + + ACE_Write_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->tryacquire_write (); +} + +template int +ACE_TSS_Write_Guard::acquire_write (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::acquire_write"); + + return this->acquire (); +} + +template int +ACE_TSS_Write_Guard::tryacquire_write (void) +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::tryacquire_write"); + + return this->tryacquire (); +} + +template void +ACE_TSS_Write_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_TSS_Write_Guard::dump"); + ACE_TSS_Guard::dump (); +} + +template +ACE_TSS_Read_Guard::ACE_TSS_Read_Guard (ACE_LOCK &lock, int block) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::ACE_TSS_Read_Guard"); + + this->init_key (); + ACE_Guard *guard; + ACE_NEW (guard, + ACE_Read_Guard (lock, + block)); +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter; + ACE_NEW (tss_adapter, + ACE_TSS_Adapter ((void *)guard, + ACE_TSS_Guard::cleanup)); + ACE_Thread::setspecific (this->key_, + (void *) tss_adapter); +#else + ACE_Thread::setspecific (this->key_, + (void *) guard); +#endif /* ACE_HAS_THR_C_DEST */ +} + +template int +ACE_TSS_Read_Guard::acquire (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::acquire"); + + ACE_Read_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->acquire_read (); +} + +template int +ACE_TSS_Read_Guard::tryacquire (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::tryacquire"); + + ACE_Read_Guard *guard = 0; + +#if defined (ACE_HAS_THR_C_DEST) + ACE_TSS_Adapter *tss_adapter = 0; + ACE_Thread::getspecific (this->key_, + (void **) &tss_adapter); + guard = (ACE_Guard *) tss_adapter->ts_obj_; +#else + ACE_Thread::getspecific (this->key_, + (void **) &guard); +#endif /* ACE_HAS_THR_C_DEST */ + + return guard->tryacquire_read (); +} + +template int +ACE_TSS_Read_Guard::acquire_read (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::acquire_read"); + + return this->acquire (); +} + +template int +ACE_TSS_Read_Guard::tryacquire_read (void) +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::tryacquire_read"); + + return this->tryacquire (); +} + +template void +ACE_TSS_Read_Guard::dump (void) const +{ +// ACE_TRACE ("ACE_TSS_Read_Guard::dump"); + ACE_TSS_Guard::dump (); +} + + +#endif /* defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) */ + +#endif /* ACE_SYNCH_T_C */ diff --git a/ace/Threads/Synch_T.h b/ace/Threads/Synch_T.h new file mode 100644 index 00000000000..e33503a9bc3 --- /dev/null +++ b/ace/Threads/Synch_T.h @@ -0,0 +1,903 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Synch_T.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SYNCH_T_H +#define ACE_SYNCH_T_H +#include "ace/pre.h" +#include "ace/Threads/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward decl +class ACE_Time_Value; + +/** + * @class ACE_Lock_Adapter + * + * @brief This is an adapter that allows applications to transparently + * combine the abstract base class (which contains + * pure virtual methods) with any of the other concrete ACE + * synchronization classes (e.g., , , + * , etc.). + * + * This class uses a form of the Adapter pattern. + */ +template +class ACE_Lock_Adapter : public ACE_Lock +{ +public: + typedef ACE_LOCKING_MECHANISM ACE_LOCK; + + // = Initialization/Finalization methods. + + /// Constructor. All locking requests will be forwarded to . + ACE_Lock_Adapter (ACE_LOCKING_MECHANISM &lock); + + /// Constructor. Since no lock is provided by the user, one will be + /// created internally. + ACE_Lock_Adapter (void); + + /// Destructor. If was not passed in by the user, it will be + /// deleted. + virtual ~ACE_Lock_Adapter (void); + + // = Lock accessors. + /// Block the thread until the lock is acquired. + virtual int acquire (void); + + /// Conditionally acquire the lock (i.e., won't block). + virtual int tryacquire (void); + + /// Release the lock. + virtual int release (void); + + /** + * Block until the thread acquires a read lock. If the locking + * mechanism doesn't support read locks then this just calls + * . + */ + virtual int acquire_read (void); + + /** + * Block until the thread acquires a write lock. If the locking + * mechanism doesn't support read locks then this just calls + * . + */ + virtual int acquire_write (void); + + /// Conditionally acquire a read lock. If the locking mechanism + /// doesn't support read locks then this just calls . + virtual int tryacquire_read (void); + + /// Conditionally acquire a write lock. If the locking mechanism + /// doesn't support read locks then this just calls . + virtual int tryacquire_write (void); + + /** + * Conditionally try to upgrade a lock held for read to a write lock. + * If the locking mechanism doesn't support read locks then this just + * calls . Returns 0 on success, -1 on failure. + */ + virtual int tryacquire_write_upgrade (void); + + /// Explicitly destroy the lock. + virtual int remove (void); + +private: + /// The concrete locking mechanism that all the methods delegate to. + ACE_LOCKING_MECHANISM *lock_; + + /// This flag keep track of whether we are responsible for deleting + /// the lock + int delete_lock_; +}; + +/** + * @class ACE_Acquire_Method + * + * @brief An enum class. + * + * These enums should have been inside the reverse lock class, but + * some lame compilers cannot handle enums inside template classes. + * + * The METHOD_TYPE is used to indicate which acquire() method will be + * called on the real lock when the release() method is called on the + * reverse lock. REGULAR indicated the acquire() method, READ + * indicates the acquire_read() method, and WRITE indicates the + * acquire_write() method. Note that the try_*() methods are not + * represented here because we have to make sure that the release() + * method on the reverse lock acquires a lock on the real lock. + **/ +class ACE_Acquire_Method +{ +public: + enum METHOD_TYPE + { + ACE_REGULAR, + ACE_READ, + ACE_WRITE + }; +}; + +/** + * @class ACE_Reverse_Lock + * + * @brief A reverse (or anti) lock. + * + * This is an interesting adapter class that changes a lock into + * a reverse lock, i.e., on this class calls + * on the lock, and on this class calls on + * the lock. + * One motivation for this class is when we temporarily want to + * release a lock (which we have already acquired) but then + * reacquire it soon after. An alternative design would be to + * add a Anti_Guard or Reverse_Guard class which would + * on construction and destruction. However, there + * are *many* varieties of the Guard class and this design + * choice would lead to at least 6 new classes. One new + * ACE_Reverse_Lock class seemed more reasonable. + */ +template +class ACE_Reverse_Lock : public ACE_Lock +{ +public: + + typedef ACE_LOCKING_MECHANISM ACE_LOCK; + + // = Initialization/Finalization methods. + + /// Constructor. All locking requests will be forwarded to . + ACE_Reverse_Lock (ACE_LOCKING_MECHANISM &lock, + ACE_Acquire_Method::METHOD_TYPE acquire_method = ACE_Acquire_Method::ACE_REGULAR); + + /// Destructor. If was not passed in by the user, it will be + /// deleted. + virtual ~ACE_Reverse_Lock (void); + + // = Lock accessors. + /// Release the lock. + virtual int acquire (void); + + /// Release the lock. + virtual int tryacquire (void); + + /// Acquire the lock. + virtual int release (void); + + /// Release the lock. + virtual int acquire_read (void); + + /// Release the lock. + virtual int acquire_write (void); + + /// Release the lock. + virtual int tryacquire_read (void); + + /// Release the lock. + virtual int tryacquire_write (void); + + /// Release the lock. + virtual int tryacquire_write_upgrade (void); + + /// Explicitly destroy the lock. + virtual int remove (void); + +private: + /// The concrete locking mechanism that all the methods delegate to. + ACE_LOCKING_MECHANISM &lock_; + + /// This indicates what kind of acquire method will be called. + ACE_Acquire_Method::METHOD_TYPE acquire_method_; +}; + +/** + * @class ACE_TSS + * + * @brief Allows objects that are "physically" in thread specific + * storage (i.e., private to a thread) to be accessed as though + * they were "logically" global to a program. + * + * This class is a wrapper around the OS thread library + * thread-specific functions. It uses the > to + * shield applications from the details of accessing + * thread-specific storage. + * + * NOTE: For maximal portability, cannot be a built-in type, + * but instead should be a user-defined class (some compilers will + * allow a built-in type, others won't). See template class + * ACE_TSS_Type_Adapter, below, for adapting built-in types to work + * with ACE_TSS. + */ +template +class ACE_TSS +{ +public: + // = Initialization and termination methods. + + /** + * If caller has passed us a non-NULL ts_obj *, then we'll just use + * this to initialize the thread-specific value (but only for the + * calling thread). Thus, subsequent calls to > in this + * thread will return this value. This is useful since it enables + * us to assign objects to thread-specific data that have + * arbitrarily complex constructors. + */ + ACE_TSS (TYPE *ts_obj = 0); + + /// Deregister with thread-key administration. + virtual ~ACE_TSS (void); + + // = Accessors. + + /** + * Get the thread-specific object for the key associated with this + * object. Returns 0 if the data has never been initialized, + * otherwise returns a pointer to the data. + */ + TYPE *ts_object (void) const; + + /// Set the thread-specific object for the key associated with this + /// object. + TYPE *ts_object (TYPE *); + + /// Use a "smart pointer" to get the thread-specific object + /// associated with the . + TYPE *operator-> () const; + + /// Return or create and return the calling threads TYPE object. + operator TYPE *(void) const; + + /// Hook for construction parameters. + virtual TYPE *make_TSS_TYPE (void) const; + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + /// Actually implements the code that retrieves the object from + /// thread-specific storage. + TYPE *ts_get (void) const; + + /// Factors out common code for initializing TSS. This must NOT be + /// called with the lock held... + int ts_init (void) const; + +#if !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) + /// This implementation only works for non-threading systems... + TYPE *type_; +#else + /// Avoid race conditions during initialization. + ACE_Thread_Mutex keylock_; + + /// "First time in" flag. + int once_; + + /// Key for the thread-specific error data. + ACE_thread_key_t key_; + + /// "Destructor" that deletes internal TYPE * when thread exits. + static void cleanup (void *ptr); +#endif /* defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) */ + // = Disallow copying... + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_TSS &)) + ACE_UNIMPLEMENTED_FUNC (ACE_TSS (const ACE_TSS &)) +}; + +/** + * @class ACE_TSS_Type_Adapter + * + * @brief Adapter that allows built-in types to be used with ACE_TSS. + * + * Wraps a value of a built-in type, providing conversions to + * and from the type. Example use with ACE_TSS: + * ACE_TSS > i; + * *i = 37; + * ACE_OS::fprintf (stderr, "%d\n", *i); + * Unfortunately, though, some compilers have trouble with the + * implicit type conversions. This seems to work better: + * ACE_TSS > i; + * i->operator int & () = 37; + * ACE_OS::fprintf (stderr, "%d\n", i->operator int ()); + */ +template +class ACE_TSS_Type_Adapter +{ +public: + /// Constructor. Inlined here so that it should _always_ be inlined. + ACE_TSS_Type_Adapter (const TYPE value = 0): value_ (value) {} + + /// TYPE conversion. Inlined here so that it should _always_ be + /// inlined. + operator TYPE () const { return value_; }; + + /// TYPE & conversion. Inlined here so that it should _always_ be + /// inlined. + operator TYPE &() { return value_; }; + +private: + /// The wrapped value. + TYPE value_; +}; + +/** + * @class ACE_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * a parameterized synchronization object . + * + * The class given as an actual parameter must provide at + * the very least the , , , and + * methods. + */ +template +class ACE_Guard +{ +public: + + // = Initialization and termination methods. + ACE_Guard (ACE_LOCK &l); + + /// Implicitly and automatically acquire (or try to acquire) the + /// lock. If is non-0 then the , else + /// it. + ACE_Guard (ACE_LOCK &l, int block); + + /// Initialise the guard without implicitly acquiring the lock. The + /// parameter indicates whether the guard should release + /// the lock implicitly on destruction. The parameter is + /// ignored and is used here to disambiguate with the preceding + /// constructor. + ACE_Guard (ACE_LOCK &l, int block, int become_owner); + + /// Implicitly release the lock. + ~ACE_Guard (void); + + // = Lock accessors. + + /// Explicitly acquire the lock. + int acquire (void); + + /// Conditionally acquire the lock (i.e., won't block). + int tryacquire (void); + + /// Explicitly release the lock, but only if it is held! + int release (void); + + /// Relinquish ownership of the lock so that it is not released + /// implicitly in the destructor. + void disown (void); + + // = Utility methods. + /// 1 if locked, 0 if couldn't acquire the lock + /// (errno will contain the reason for this). + int locked (void) const; + + /// Explicitly remove the lock. + int remove (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + + /// Helper, meant for subclass only. + ACE_Guard (ACE_LOCK *lock): lock_ (lock) {} + + /// Pointer to the ACE_LOCK we're guarding. + ACE_LOCK *lock_; + + /// Keeps track of whether we acquired the lock or failed. + int owner_; + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Guard &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Guard (const ACE_Guard &)) +}; + +/** + * @class ACE_Write_Guard + * + * @brief This class is similar to class , though it + * acquires/releases a write lock automatically (naturally, the + * it is instantiated with must support the appropriate + * API). + */ +template +class ACE_Write_Guard : public ACE_Guard +{ +public: + // = Initialization method. + + /// Implicitly and automatically acquire a write lock. + ACE_Write_Guard (ACE_LOCK &m); + + /// Implicitly and automatically acquire (or try to acquire) a write + /// lock. + ACE_Write_Guard (ACE_LOCK &m, int block); + + // = Lock accessors. + + /// Explicitly acquire the write lock. + int acquire_write (void); + + /// Explicitly acquire the write lock. + int acquire (void); + + /// Conditionally acquire the write lock (i.e., won't block). + int tryacquire_write (void); + + /// Conditionally acquire the write lock (i.e., won't block). + int tryacquire (void); + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +/** + * @class ACE_Read_Guard + * + * @brief This class is similar to class , though it + * acquires/releases a read lock automatically (naturally, the + * it is instantiated with must support the appropriate + * API). + */ +template +class ACE_Read_Guard : public ACE_Guard +{ +public: + // = Initialization methods. + + /// Implicitly and automatically acquire a read lock. + ACE_Read_Guard (ACE_LOCK& m); + + /// Implicitly and automatically acquire (or try to acquire) a read + /// lock. + ACE_Read_Guard (ACE_LOCK &m, int block); + + // = Lock accessors. + + /// Explicitly acquire the read lock. + int acquire_read (void); + + /// Explicitly acquire the read lock. + int acquire (void); + + /// Conditionally acquire the read lock (i.e., won't block). + int tryacquire_read (void); + + /// Conditionally acquire the read lock (i.e., won't block). + int tryacquire (void); + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +#if !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) + +#define ACE_TSS_Guard ACE_Guard +#define ACE_TSS_Write_GUARD ACE_Write_Guard +#define ACE_TSS_Read_GUARD ACE_Read_Guard + +#else + /* ACE platform supports some form of threading and + thread-specific storage. */ + +/** + * @class ACE_TSS_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * a synchronization object. Moreover, it ensures that the lock + * is released even if a thread exits via ! + */ +template +class ACE_TSS_Guard +{ +public: + // = Initialization and termination methods. + + /// Implicitly and automatically acquire the thread-specific lock. + ACE_TSS_Guard (ACE_LOCK &lock, int block = 1); + + /// Implicitly release the thread-specific lock. + ~ACE_TSS_Guard (void); + + // = Lock accessors. + + /// Explicitly acquire the thread-specific lock. + int acquire (void); + + /// Conditionally acquire the thread-specific lock (i.e., won't + /// block). + int tryacquire (void); + + /// Explicitly release the thread-specific lock. + int release (void); + + // = Utility methods. + /// Explicitly release the thread-specific lock. + int remove (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + /// Helper, meant for subclass only. + ACE_TSS_Guard (void); + + /// Initialize the key. + void init_key (void); + + /// Called when thread exits to clean up the lock. + static void cleanup (void *ptr); + + /// Thread-specific key... + ACE_thread_key_t key_; + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_TSS_Guard &)) + ACE_UNIMPLEMENTED_FUNC (ACE_TSS_Guard (const ACE_TSS_Guard &)) +}; + +/** + * @class ACE_TSS_Write_Guard + * + * @brief This class is similar to class ACE_TSS_Guard, though it + * acquires/releases a write-lock automatically (naturally, the + * ACE_LOCK it is instantiated with must support the appropriate + * API). + */ +template +class ACE_TSS_Write_Guard : public ACE_TSS_Guard +{ +public: + // = Initialization method. + + /// Implicitly and automatically acquire the thread-specific write lock. + ACE_TSS_Write_Guard (ACE_LOCK &lock, int block = 1); + + // = Lock accessors. + + /// Explicitly acquire the thread-specific write lock. + int acquire_write (void); + + /// Explicitly acquire the thread-specific write lock. + int acquire (void); + + /// Conditionally acquire the thread-specific write lock (i.e., won't block). + int tryacquire_write (void); + + /// Conditionally acquire the thread-specific write lock (i.e., won't block). + int tryacquire (void); + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +/** + * @class ACE_TSS_Read_Guard + * + * @brief This class is similar to class , though it + * acquires/releases a read lock automatically (naturally, the + * it is instantiated with must support the + * appropriate API). + */ +template +class ACE_TSS_Read_Guard : public ACE_TSS_Guard +{ +public: + // = Initialization method. + /// Implicitly and automatically acquire the thread-specific read lock. + ACE_TSS_Read_Guard (ACE_LOCK &lock, int block = 1); + + // = Lock accessors. + /// Explicitly acquire the thread-specific read lock. + int acquire_read (void); + + /// Explicitly acquire the thread-specific read lock. + int acquire (void); + + /// Conditionally acquire the thread-specific read lock (i.e., won't + /// block). + int tryacquire_read (void); + + /// Conditionally acquire the thread-specific read lock (i.e., won't + /// block). + int tryacquire (void); + + // = Utility methods. + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; +#endif /* !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) */ + +#if defined (ACE_HAS_THREADS) /* ACE platform supports some form of threading. */ + +/** + * @class ACE_Condition + * + * @brief ACE_Condition variable wrapper, which allows threads to block + * until shared data changes state. + * + * A condition variable enables threads to atomically block and + * test the condition under the protection of a mutual exclu- + * sion lock (mutex) until the condition is satisfied. That is, + * the mutex must have been held by the thread before calling + * wait or signal on the condition. If the condition is false, + * a thread blocks on a condition variable and atomically + * releases the mutex that is waiting for the condition to + * change. If another thread changes the condition, it may wake + * up waiting threads by signaling the associated condition + * variable. The waiting threads, upon awakening, reacquire the + * mutex and re-evaluate the condition. + * Note, you can only parameterize with + * or . + */ +template +class ACE_Condition +{ +public: + // = Initialiation and termination methods. + /// Initialize the condition variable. + ACE_Condition (MUTEX &m, int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, void *arg = 0); + + /// Implicitly destroy the condition variable. + ~ACE_Condition (void); + + // = Lock accessors. + /** + * Block on condition, or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" semantics. Else, if + * != 0 and the call times out before the condition is signaled + * returns -1 and sets errno to ETIME. + */ + int wait (const ACE_Time_Value *abstime); + + /// Block on condition. + int wait (void); + + /** + * Block on condition or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" wait() semantics on the + * passed as a parameter (this is useful if you need to store the + * in shared memory). Else, if != 0 and the + * call times out before the condition is signaled returns -1 + * and sets errno to ETIME. + */ + int wait (MUTEX &mutex, const ACE_Time_Value *abstime = 0); + + /// Signal one waiting thread. + int signal (void); + + /// Signal *all* waiting threads. + int broadcast (void); + + // = Utility methods. + /// Explicitly destroy the condition variable. + int remove (void); + + /// Returns a reference to the underlying mutex_; + MUTEX &mutex (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: +#if defined (CHORUS) + /// This condition resides in shared memory. + ACE_cond_t *process_cond_; + + /** + * Remember the name of the condition if we created it so we can + * unlink it when we go away (only the actor that initialized the + * memory can destroy it). + */ + const ACE_TCHAR *condname_; +#endif /* CHORUS */ + + /// Condition variable. + ACE_cond_t cond_; + + /// Reference to mutex lock. + MUTEX &mutex_; + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Condition &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Condition (const ACE_Condition &)) +}; + +/** + * @class ACE_Thread_Condition + * + * @brief ACE_Condition variable wrapper that works within processes. + * + * A condition variable enables threads to atomically block and + * test the condition under the protection of a mutual exclu- + * sion lock (mutex) until the condition is satisfied. That is, + * the mutex must have been held by the thread before calling + * wait or signal on the condition. If the condition is false, + * a thread blocks on a condition variable and atomically + * releases the mutex that is waiting for the condition to + * change. If another thread changes the condition, it may wake + * up waiting threads by signaling the associated condition + * variable. The waiting threads, upon awakening, reacquire the + * mutex and re-evaluate the condition. + */ +template +class ACE_Thread_Condition : public ACE_Condition +{ +public: + // = Initialization method. + ACE_Thread_Condition (MUTEX &m, const ACE_TCHAR *name = 0, void *arg = 0); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +#endif /* ACE_HAS_THREADS */ + +#if defined (ACE_HAS_TEMPLATE_TYPEDEFS) + +/** + * @class ACE_NULL_SYNCH + * + * @brief Implement a do nothing Synchronization wrapper that + * typedefs the and to the Null* versions. + */ +class ACE_Export ACE_NULL_SYNCH +{ +public: + typedef ACE_Null_Mutex MUTEX; + typedef ACE_Null_Mutex NULL_MUTEX; + typedef ACE_Null_Mutex PROCESS_MUTEX; + typedef ACE_Null_Mutex RECURSIVE_MUTEX; + typedef ACE_Null_Mutex RW_MUTEX; + typedef ACE_Null_Condition CONDITION; + typedef ACE_Null_Semaphore SEMAPHORE; + typedef ACE_Null_Mutex NULL_SEMAPHORE; +}; + +#if defined (ACE_HAS_THREADS) + +class ACE_Process_Mutex; + +/** + * @class ACE_MT_SYNCH + * + * @brief Implement a default thread safe synchronization wrapper that + * typedefs the and to the + * and versions. Note that this + * should be a template, but SunC++ 4.0.1 complains about + * this... + */ +class ACE_Export ACE_MT_SYNCH +{ +public: + typedef ACE_Thread_Mutex MUTEX; + typedef ACE_Null_Mutex NULL_MUTEX; + typedef ACE_Process_Mutex PROCESS_MUTEX; + typedef ACE_Recursive_Thread_Mutex RECURSIVE_MUTEX; + typedef ACE_RW_Thread_Mutex RW_MUTEX; + typedef ACE_Condition_Thread_Mutex CONDITION; + typedef ACE_Thread_Semaphore SEMAPHORE; + typedef ACE_Null_Semaphore NULL_SEMAPHORE; +}; + +#endif /* ACE_HAS_THREADS */ + +#define ACE_SYNCH_MUTEX ACE_SYNCH::MUTEX +#define ACE_SYNCH_NULL_MUTEX ACE_SYNCH::NULL_MUTEX +#define ACE_SYNCH_RECURSIVE_MUTEX ACE_SYNCH::RECURSIVE_MUTEX +#define ACE_SYNCH_RW_MUTEX ACE_SYNCH::RW_MUTEX +#define ACE_SYNCH_CONDITION ACE_SYNCH::CONDITION +#define ACE_SYNCH_NULL_SEMAPHORE ACE_SYNCH::NULL_SEMAPHORE +#define ACE_SYNCH_SEMAPHORE ACE_SYNCH::SEMAPHORE + +#else /* !ACE_HAS_TEMPLATE_TYPEDEFS */ + +#if defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) +#define ACE_NULL_SYNCH ACE_Null_Mutex, ACE_Null_Condition, ACE_Null_Mutex +#define ACE_MT_SYNCH ACE_Thread_Mutex, ACE_Condition_Thread_Mutex, ACE_Thread_Semaphore +#else +#define ACE_NULL_SYNCH ACE_Null_Mutex, ACE_Null_Condition +#define ACE_MT_SYNCH ACE_Thread_Mutex, ACE_Condition_Thread_Mutex +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + +#if defined (ACE_HAS_THREADS) + +#define ACE_SYNCH_MUTEX ACE_Thread_Mutex +#define ACE_SYNCH_NULL_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_RECURSIVE_MUTEX ACE_Recursive_Thread_Mutex +#define ACE_SYNCH_RW_MUTEX ACE_RW_Thread_Mutex +#define ACE_SYNCH_CONDITION ACE_Condition_Thread_Mutex +#define ACE_SYNCH_SEMAPHORE ACE_Thread_Semaphore +#define ACE_SYNCH_NULL_SEMAPHORE ACE_Null_Semaphore + +#else /* ACE_HAS_THREADS */ + +#define ACE_SYNCH_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_NULL_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_RECURSIVE_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_RW_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_CONDITION ACE_Null_Condition +#define ACE_SYNCH_SEMAPHORE ACE_Null_Semaphore +#define ACE_SYNCH_NULL_SEMAPHORE ACE_Null_Mutex + +#endif /* ACE_HAS_THREADS */ +#endif /* ACE_HAS_TEMPLATE_TYPEDEFS */ + +// These are available on *all* platforms +#define ACE_SYNCH_PROCESS_SEMAPHORE ACE_Process_Semaphore +#define ACE_SYNCH_PROCESS_MUTEX ACE_Process_Mutex + +#if defined (ACE_HAS_THREADS) +#define ACE_SYNCH ACE_MT_SYNCH +#else /* ACE_HAS_THREADS */ +#define ACE_SYNCH ACE_NULL_SYNCH +#endif /* ACE_HAS_THREADS */ + +#if defined (__ACE_INLINE__) +#include "ace/Threads/Synch_T.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Threads/Synch_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Synch_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_SYNCH_T_H */ diff --git a/ace/Threads/Synch_T.h~ b/ace/Threads/Synch_T.h~ new file mode 100644 index 00000000000..74b3170f0ac --- /dev/null +++ b/ace/Threads/Synch_T.h~ @@ -0,0 +1,903 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Synch_T.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_SYNCH_T_H +#define ACE_SYNCH_T_H +#include "ace/pre.h" +#include "ace/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward decl +class ACE_Time_Value; + +/** + * @class ACE_Lock_Adapter + * + * @brief This is an adapter that allows applications to transparently + * combine the abstract base class (which contains + * pure virtual methods) with any of the other concrete ACE + * synchronization classes (e.g., , , + * , etc.). + * + * This class uses a form of the Adapter pattern. + */ +template +class ACE_Lock_Adapter : public ACE_Lock +{ +public: + typedef ACE_LOCKING_MECHANISM ACE_LOCK; + + // = Initialization/Finalization methods. + + /// Constructor. All locking requests will be forwarded to . + ACE_Lock_Adapter (ACE_LOCKING_MECHANISM &lock); + + /// Constructor. Since no lock is provided by the user, one will be + /// created internally. + ACE_Lock_Adapter (void); + + /// Destructor. If was not passed in by the user, it will be + /// deleted. + virtual ~ACE_Lock_Adapter (void); + + // = Lock accessors. + /// Block the thread until the lock is acquired. + virtual int acquire (void); + + /// Conditionally acquire the lock (i.e., won't block). + virtual int tryacquire (void); + + /// Release the lock. + virtual int release (void); + + /** + * Block until the thread acquires a read lock. If the locking + * mechanism doesn't support read locks then this just calls + * . + */ + virtual int acquire_read (void); + + /** + * Block until the thread acquires a write lock. If the locking + * mechanism doesn't support read locks then this just calls + * . + */ + virtual int acquire_write (void); + + /// Conditionally acquire a read lock. If the locking mechanism + /// doesn't support read locks then this just calls . + virtual int tryacquire_read (void); + + /// Conditionally acquire a write lock. If the locking mechanism + /// doesn't support read locks then this just calls . + virtual int tryacquire_write (void); + + /** + * Conditionally try to upgrade a lock held for read to a write lock. + * If the locking mechanism doesn't support read locks then this just + * calls . Returns 0 on success, -1 on failure. + */ + virtual int tryacquire_write_upgrade (void); + + /// Explicitly destroy the lock. + virtual int remove (void); + +private: + /// The concrete locking mechanism that all the methods delegate to. + ACE_LOCKING_MECHANISM *lock_; + + /// This flag keep track of whether we are responsible for deleting + /// the lock + int delete_lock_; +}; + +/** + * @class ACE_Acquire_Method + * + * @brief An enum class. + * + * These enums should have been inside the reverse lock class, but + * some lame compilers cannot handle enums inside template classes. + * + * The METHOD_TYPE is used to indicate which acquire() method will be + * called on the real lock when the release() method is called on the + * reverse lock. REGULAR indicated the acquire() method, READ + * indicates the acquire_read() method, and WRITE indicates the + * acquire_write() method. Note that the try_*() methods are not + * represented here because we have to make sure that the release() + * method on the reverse lock acquires a lock on the real lock. + **/ +class ACE_Acquire_Method +{ +public: + enum METHOD_TYPE + { + ACE_REGULAR, + ACE_READ, + ACE_WRITE + }; +}; + +/** + * @class ACE_Reverse_Lock + * + * @brief A reverse (or anti) lock. + * + * This is an interesting adapter class that changes a lock into + * a reverse lock, i.e., on this class calls + * on the lock, and on this class calls on + * the lock. + * One motivation for this class is when we temporarily want to + * release a lock (which we have already acquired) but then + * reacquire it soon after. An alternative design would be to + * add a Anti_Guard or Reverse_Guard class which would + * on construction and destruction. However, there + * are *many* varieties of the Guard class and this design + * choice would lead to at least 6 new classes. One new + * ACE_Reverse_Lock class seemed more reasonable. + */ +template +class ACE_Reverse_Lock : public ACE_Lock +{ +public: + + typedef ACE_LOCKING_MECHANISM ACE_LOCK; + + // = Initialization/Finalization methods. + + /// Constructor. All locking requests will be forwarded to . + ACE_Reverse_Lock (ACE_LOCKING_MECHANISM &lock, + ACE_Acquire_Method::METHOD_TYPE acquire_method = ACE_Acquire_Method::ACE_REGULAR); + + /// Destructor. If was not passed in by the user, it will be + /// deleted. + virtual ~ACE_Reverse_Lock (void); + + // = Lock accessors. + /// Release the lock. + virtual int acquire (void); + + /// Release the lock. + virtual int tryacquire (void); + + /// Acquire the lock. + virtual int release (void); + + /// Release the lock. + virtual int acquire_read (void); + + /// Release the lock. + virtual int acquire_write (void); + + /// Release the lock. + virtual int tryacquire_read (void); + + /// Release the lock. + virtual int tryacquire_write (void); + + /// Release the lock. + virtual int tryacquire_write_upgrade (void); + + /// Explicitly destroy the lock. + virtual int remove (void); + +private: + /// The concrete locking mechanism that all the methods delegate to. + ACE_LOCKING_MECHANISM &lock_; + + /// This indicates what kind of acquire method will be called. + ACE_Acquire_Method::METHOD_TYPE acquire_method_; +}; + +/** + * @class ACE_TSS + * + * @brief Allows objects that are "physically" in thread specific + * storage (i.e., private to a thread) to be accessed as though + * they were "logically" global to a program. + * + * This class is a wrapper around the OS thread library + * thread-specific functions. It uses the > to + * shield applications from the details of accessing + * thread-specific storage. + * + * NOTE: For maximal portability, cannot be a built-in type, + * but instead should be a user-defined class (some compilers will + * allow a built-in type, others won't). See template class + * ACE_TSS_Type_Adapter, below, for adapting built-in types to work + * with ACE_TSS. + */ +template +class ACE_TSS +{ +public: + // = Initialization and termination methods. + + /** + * If caller has passed us a non-NULL ts_obj *, then we'll just use + * this to initialize the thread-specific value (but only for the + * calling thread). Thus, subsequent calls to > in this + * thread will return this value. This is useful since it enables + * us to assign objects to thread-specific data that have + * arbitrarily complex constructors. + */ + ACE_TSS (TYPE *ts_obj = 0); + + /// Deregister with thread-key administration. + virtual ~ACE_TSS (void); + + // = Accessors. + + /** + * Get the thread-specific object for the key associated with this + * object. Returns 0 if the data has never been initialized, + * otherwise returns a pointer to the data. + */ + TYPE *ts_object (void) const; + + /// Set the thread-specific object for the key associated with this + /// object. + TYPE *ts_object (TYPE *); + + /// Use a "smart pointer" to get the thread-specific object + /// associated with the . + TYPE *operator-> () const; + + /// Return or create and return the calling threads TYPE object. + operator TYPE *(void) const; + + /// Hook for construction parameters. + virtual TYPE *make_TSS_TYPE (void) const; + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + /// Actually implements the code that retrieves the object from + /// thread-specific storage. + TYPE *ts_get (void) const; + + /// Factors out common code for initializing TSS. This must NOT be + /// called with the lock held... + int ts_init (void) const; + +#if !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) + /// This implementation only works for non-threading systems... + TYPE *type_; +#else + /// Avoid race conditions during initialization. + ACE_Thread_Mutex keylock_; + + /// "First time in" flag. + int once_; + + /// Key for the thread-specific error data. + ACE_thread_key_t key_; + + /// "Destructor" that deletes internal TYPE * when thread exits. + static void cleanup (void *ptr); +#endif /* defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION)) */ + // = Disallow copying... + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_TSS &)) + ACE_UNIMPLEMENTED_FUNC (ACE_TSS (const ACE_TSS &)) +}; + +/** + * @class ACE_TSS_Type_Adapter + * + * @brief Adapter that allows built-in types to be used with ACE_TSS. + * + * Wraps a value of a built-in type, providing conversions to + * and from the type. Example use with ACE_TSS: + * ACE_TSS > i; + * *i = 37; + * ACE_OS::fprintf (stderr, "%d\n", *i); + * Unfortunately, though, some compilers have trouble with the + * implicit type conversions. This seems to work better: + * ACE_TSS > i; + * i->operator int & () = 37; + * ACE_OS::fprintf (stderr, "%d\n", i->operator int ()); + */ +template +class ACE_TSS_Type_Adapter +{ +public: + /// Constructor. Inlined here so that it should _always_ be inlined. + ACE_TSS_Type_Adapter (const TYPE value = 0): value_ (value) {} + + /// TYPE conversion. Inlined here so that it should _always_ be + /// inlined. + operator TYPE () const { return value_; }; + + /// TYPE & conversion. Inlined here so that it should _always_ be + /// inlined. + operator TYPE &() { return value_; }; + +private: + /// The wrapped value. + TYPE value_; +}; + +/** + * @class ACE_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * a parameterized synchronization object . + * + * The class given as an actual parameter must provide at + * the very least the , , , and + * methods. + */ +template +class ACE_Guard +{ +public: + + // = Initialization and termination methods. + ACE_Guard (ACE_LOCK &l); + + /// Implicitly and automatically acquire (or try to acquire) the + /// lock. If is non-0 then the , else + /// it. + ACE_Guard (ACE_LOCK &l, int block); + + /// Initialise the guard without implicitly acquiring the lock. The + /// parameter indicates whether the guard should release + /// the lock implicitly on destruction. The parameter is + /// ignored and is used here to disambiguate with the preceding + /// constructor. + ACE_Guard (ACE_LOCK &l, int block, int become_owner); + + /// Implicitly release the lock. + ~ACE_Guard (void); + + // = Lock accessors. + + /// Explicitly acquire the lock. + int acquire (void); + + /// Conditionally acquire the lock (i.e., won't block). + int tryacquire (void); + + /// Explicitly release the lock, but only if it is held! + int release (void); + + /// Relinquish ownership of the lock so that it is not released + /// implicitly in the destructor. + void disown (void); + + // = Utility methods. + /// 1 if locked, 0 if couldn't acquire the lock + /// (errno will contain the reason for this). + int locked (void) const; + + /// Explicitly remove the lock. + int remove (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + + /// Helper, meant for subclass only. + ACE_Guard (ACE_LOCK *lock): lock_ (lock) {} + + /// Pointer to the ACE_LOCK we're guarding. + ACE_LOCK *lock_; + + /// Keeps track of whether we acquired the lock or failed. + int owner_; + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Guard &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Guard (const ACE_Guard &)) +}; + +/** + * @class ACE_Write_Guard + * + * @brief This class is similar to class , though it + * acquires/releases a write lock automatically (naturally, the + * it is instantiated with must support the appropriate + * API). + */ +template +class ACE_Write_Guard : public ACE_Guard +{ +public: + // = Initialization method. + + /// Implicitly and automatically acquire a write lock. + ACE_Write_Guard (ACE_LOCK &m); + + /// Implicitly and automatically acquire (or try to acquire) a write + /// lock. + ACE_Write_Guard (ACE_LOCK &m, int block); + + // = Lock accessors. + + /// Explicitly acquire the write lock. + int acquire_write (void); + + /// Explicitly acquire the write lock. + int acquire (void); + + /// Conditionally acquire the write lock (i.e., won't block). + int tryacquire_write (void); + + /// Conditionally acquire the write lock (i.e., won't block). + int tryacquire (void); + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +/** + * @class ACE_Read_Guard + * + * @brief This class is similar to class , though it + * acquires/releases a read lock automatically (naturally, the + * it is instantiated with must support the appropriate + * API). + */ +template +class ACE_Read_Guard : public ACE_Guard +{ +public: + // = Initialization methods. + + /// Implicitly and automatically acquire a read lock. + ACE_Read_Guard (ACE_LOCK& m); + + /// Implicitly and automatically acquire (or try to acquire) a read + /// lock. + ACE_Read_Guard (ACE_LOCK &m, int block); + + // = Lock accessors. + + /// Explicitly acquire the read lock. + int acquire_read (void); + + /// Explicitly acquire the read lock. + int acquire (void); + + /// Conditionally acquire the read lock (i.e., won't block). + int tryacquire_read (void); + + /// Conditionally acquire the read lock (i.e., won't block). + int tryacquire (void); + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +#if !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) + +#define ACE_TSS_Guard ACE_Guard +#define ACE_TSS_Write_GUARD ACE_Write_Guard +#define ACE_TSS_Read_GUARD ACE_Read_Guard + +#else + /* ACE platform supports some form of threading and + thread-specific storage. */ + +/** + * @class ACE_TSS_Guard + * + * @brief This data structure is meant to be used within a method or + * function... It performs automatic aquisition and release of + * a synchronization object. Moreover, it ensures that the lock + * is released even if a thread exits via ! + */ +template +class ACE_TSS_Guard +{ +public: + // = Initialization and termination methods. + + /// Implicitly and automatically acquire the thread-specific lock. + ACE_TSS_Guard (ACE_LOCK &lock, int block = 1); + + /// Implicitly release the thread-specific lock. + ~ACE_TSS_Guard (void); + + // = Lock accessors. + + /// Explicitly acquire the thread-specific lock. + int acquire (void); + + /// Conditionally acquire the thread-specific lock (i.e., won't + /// block). + int tryacquire (void); + + /// Explicitly release the thread-specific lock. + int release (void); + + // = Utility methods. + /// Explicitly release the thread-specific lock. + int remove (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + /// Helper, meant for subclass only. + ACE_TSS_Guard (void); + + /// Initialize the key. + void init_key (void); + + /// Called when thread exits to clean up the lock. + static void cleanup (void *ptr); + + /// Thread-specific key... + ACE_thread_key_t key_; + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_TSS_Guard &)) + ACE_UNIMPLEMENTED_FUNC (ACE_TSS_Guard (const ACE_TSS_Guard &)) +}; + +/** + * @class ACE_TSS_Write_Guard + * + * @brief This class is similar to class ACE_TSS_Guard, though it + * acquires/releases a write-lock automatically (naturally, the + * ACE_LOCK it is instantiated with must support the appropriate + * API). + */ +template +class ACE_TSS_Write_Guard : public ACE_TSS_Guard +{ +public: + // = Initialization method. + + /// Implicitly and automatically acquire the thread-specific write lock. + ACE_TSS_Write_Guard (ACE_LOCK &lock, int block = 1); + + // = Lock accessors. + + /// Explicitly acquire the thread-specific write lock. + int acquire_write (void); + + /// Explicitly acquire the thread-specific write lock. + int acquire (void); + + /// Conditionally acquire the thread-specific write lock (i.e., won't block). + int tryacquire_write (void); + + /// Conditionally acquire the thread-specific write lock (i.e., won't block). + int tryacquire (void); + + // = Utility methods. + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +/** + * @class ACE_TSS_Read_Guard + * + * @brief This class is similar to class , though it + * acquires/releases a read lock automatically (naturally, the + * it is instantiated with must support the + * appropriate API). + */ +template +class ACE_TSS_Read_Guard : public ACE_TSS_Guard +{ +public: + // = Initialization method. + /// Implicitly and automatically acquire the thread-specific read lock. + ACE_TSS_Read_Guard (ACE_LOCK &lock, int block = 1); + + // = Lock accessors. + /// Explicitly acquire the thread-specific read lock. + int acquire_read (void); + + /// Explicitly acquire the thread-specific read lock. + int acquire (void); + + /// Conditionally acquire the thread-specific read lock (i.e., won't + /// block). + int tryacquire_read (void); + + /// Conditionally acquire the thread-specific read lock (i.e., won't + /// block). + int tryacquire (void); + + // = Utility methods. + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; +#endif /* !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) */ + +#if defined (ACE_HAS_THREADS) /* ACE platform supports some form of threading. */ + +/** + * @class ACE_Condition + * + * @brief ACE_Condition variable wrapper, which allows threads to block + * until shared data changes state. + * + * A condition variable enables threads to atomically block and + * test the condition under the protection of a mutual exclu- + * sion lock (mutex) until the condition is satisfied. That is, + * the mutex must have been held by the thread before calling + * wait or signal on the condition. If the condition is false, + * a thread blocks on a condition variable and atomically + * releases the mutex that is waiting for the condition to + * change. If another thread changes the condition, it may wake + * up waiting threads by signaling the associated condition + * variable. The waiting threads, upon awakening, reacquire the + * mutex and re-evaluate the condition. + * Note, you can only parameterize with + * or . + */ +template +class ACE_Condition +{ +public: + // = Initialiation and termination methods. + /// Initialize the condition variable. + ACE_Condition (MUTEX &m, int type = USYNC_THREAD, + const ACE_TCHAR *name = 0, void *arg = 0); + + /// Implicitly destroy the condition variable. + ~ACE_Condition (void); + + // = Lock accessors. + /** + * Block on condition, or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" semantics. Else, if + * != 0 and the call times out before the condition is signaled + * returns -1 and sets errno to ETIME. + */ + int wait (const ACE_Time_Value *abstime); + + /// Block on condition. + int wait (void); + + /** + * Block on condition or until absolute time-of-day has passed. If + * abstime == 0 use "blocking" wait() semantics on the + * passed as a parameter (this is useful if you need to store the + * in shared memory). Else, if != 0 and the + * call times out before the condition is signaled returns -1 + * and sets errno to ETIME. + */ + int wait (MUTEX &mutex, const ACE_Time_Value *abstime = 0); + + /// Signal one waiting thread. + int signal (void); + + /// Signal *all* waiting threads. + int broadcast (void); + + // = Utility methods. + /// Explicitly destroy the condition variable. + int remove (void); + + /// Returns a reference to the underlying mutex_; + MUTEX &mutex (void); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: +#if defined (CHORUS) + /// This condition resides in shared memory. + ACE_cond_t *process_cond_; + + /** + * Remember the name of the condition if we created it so we can + * unlink it when we go away (only the actor that initialized the + * memory can destroy it). + */ + const ACE_TCHAR *condname_; +#endif /* CHORUS */ + + /// Condition variable. + ACE_cond_t cond_; + + /// Reference to mutex lock. + MUTEX &mutex_; + +private: + // = Prevent assignment and initialization. + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Condition &)) + ACE_UNIMPLEMENTED_FUNC (ACE_Condition (const ACE_Condition &)) +}; + +/** + * @class ACE_Thread_Condition + * + * @brief ACE_Condition variable wrapper that works within processes. + * + * A condition variable enables threads to atomically block and + * test the condition under the protection of a mutual exclu- + * sion lock (mutex) until the condition is satisfied. That is, + * the mutex must have been held by the thread before calling + * wait or signal on the condition. If the condition is false, + * a thread blocks on a condition variable and atomically + * releases the mutex that is waiting for the condition to + * change. If another thread changes the condition, it may wake + * up waiting threads by signaling the associated condition + * variable. The waiting threads, upon awakening, reacquire the + * mutex and re-evaluate the condition. + */ +template +class ACE_Thread_Condition : public ACE_Condition +{ +public: + // = Initialization method. + ACE_Thread_Condition (MUTEX &m, const ACE_TCHAR *name = 0, void *arg = 0); + + /// Dump the state of an object. + void dump (void) const; + + // ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. +}; + +#endif /* ACE_HAS_THREADS */ + +#if defined (ACE_HAS_TEMPLATE_TYPEDEFS) + +/** + * @class ACE_NULL_SYNCH + * + * @brief Implement a do nothing Synchronization wrapper that + * typedefs the and to the Null* versions. + */ +class ACE_Export ACE_NULL_SYNCH +{ +public: + typedef ACE_Null_Mutex MUTEX; + typedef ACE_Null_Mutex NULL_MUTEX; + typedef ACE_Null_Mutex PROCESS_MUTEX; + typedef ACE_Null_Mutex RECURSIVE_MUTEX; + typedef ACE_Null_Mutex RW_MUTEX; + typedef ACE_Null_Condition CONDITION; + typedef ACE_Null_Semaphore SEMAPHORE; + typedef ACE_Null_Mutex NULL_SEMAPHORE; +}; + +#if defined (ACE_HAS_THREADS) + +class ACE_Process_Mutex; + +/** + * @class ACE_MT_SYNCH + * + * @brief Implement a default thread safe synchronization wrapper that + * typedefs the and to the + * and versions. Note that this + * should be a template, but SunC++ 4.0.1 complains about + * this... + */ +class ACE_Export ACE_MT_SYNCH +{ +public: + typedef ACE_Thread_Mutex MUTEX; + typedef ACE_Null_Mutex NULL_MUTEX; + typedef ACE_Process_Mutex PROCESS_MUTEX; + typedef ACE_Recursive_Thread_Mutex RECURSIVE_MUTEX; + typedef ACE_RW_Thread_Mutex RW_MUTEX; + typedef ACE_Condition_Thread_Mutex CONDITION; + typedef ACE_Thread_Semaphore SEMAPHORE; + typedef ACE_Null_Semaphore NULL_SEMAPHORE; +}; + +#endif /* ACE_HAS_THREADS */ + +#define ACE_SYNCH_MUTEX ACE_SYNCH::MUTEX +#define ACE_SYNCH_NULL_MUTEX ACE_SYNCH::NULL_MUTEX +#define ACE_SYNCH_RECURSIVE_MUTEX ACE_SYNCH::RECURSIVE_MUTEX +#define ACE_SYNCH_RW_MUTEX ACE_SYNCH::RW_MUTEX +#define ACE_SYNCH_CONDITION ACE_SYNCH::CONDITION +#define ACE_SYNCH_NULL_SEMAPHORE ACE_SYNCH::NULL_SEMAPHORE +#define ACE_SYNCH_SEMAPHORE ACE_SYNCH::SEMAPHORE + +#else /* !ACE_HAS_TEMPLATE_TYPEDEFS */ + +#if defined (ACE_HAS_OPTIMIZED_MESSAGE_QUEUE) +#define ACE_NULL_SYNCH ACE_Null_Mutex, ACE_Null_Condition, ACE_Null_Mutex +#define ACE_MT_SYNCH ACE_Thread_Mutex, ACE_Condition_Thread_Mutex, ACE_Thread_Semaphore +#else +#define ACE_NULL_SYNCH ACE_Null_Mutex, ACE_Null_Condition +#define ACE_MT_SYNCH ACE_Thread_Mutex, ACE_Condition_Thread_Mutex +#endif /* ACE_HAS_OPTIMIZED_MESSAGE_QUEUE */ + +#if defined (ACE_HAS_THREADS) + +#define ACE_SYNCH_MUTEX ACE_Thread_Mutex +#define ACE_SYNCH_NULL_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_RECURSIVE_MUTEX ACE_Recursive_Thread_Mutex +#define ACE_SYNCH_RW_MUTEX ACE_RW_Thread_Mutex +#define ACE_SYNCH_CONDITION ACE_Condition_Thread_Mutex +#define ACE_SYNCH_SEMAPHORE ACE_Thread_Semaphore +#define ACE_SYNCH_NULL_SEMAPHORE ACE_Null_Semaphore + +#else /* ACE_HAS_THREADS */ + +#define ACE_SYNCH_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_NULL_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_RECURSIVE_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_RW_MUTEX ACE_Null_Mutex +#define ACE_SYNCH_CONDITION ACE_Null_Condition +#define ACE_SYNCH_SEMAPHORE ACE_Null_Semaphore +#define ACE_SYNCH_NULL_SEMAPHORE ACE_Null_Mutex + +#endif /* ACE_HAS_THREADS */ +#endif /* ACE_HAS_TEMPLATE_TYPEDEFS */ + +// These are available on *all* platforms +#define ACE_SYNCH_PROCESS_SEMAPHORE ACE_Process_Semaphore +#define ACE_SYNCH_PROCESS_MUTEX ACE_Process_Mutex + +#if defined (ACE_HAS_THREADS) +#define ACE_SYNCH ACE_MT_SYNCH +#else /* ACE_HAS_THREADS */ +#define ACE_SYNCH ACE_NULL_SYNCH +#endif /* ACE_HAS_THREADS */ + +#if defined (__ACE_INLINE__) +#include "ace/Synch_T.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Synch_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Synch_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_SYNCH_T_H */ diff --git a/ace/Threads/Synch_T.i b/ace/Threads/Synch_T.i new file mode 100644 index 00000000000..24f0f0a1ff7 --- /dev/null +++ b/ace/Threads/Synch_T.i @@ -0,0 +1,452 @@ +/* -*- C++ -*- */ +// $Id$ + +#include "ace/Threads/Thread.h" + +template ACE_INLINE int +ACE_Guard::acquire (void) +{ + return this->owner_ = this->lock_->acquire (); +} + +template ACE_INLINE int +ACE_Guard::tryacquire (void) +{ + return this->owner_ = this->lock_->tryacquire (); +} + +template ACE_INLINE int +ACE_Guard::release (void) +{ + if (this->owner_ == -1) + return -1; + else + { + this->owner_ = -1; + return this->lock_->release (); + } +} + +template ACE_INLINE +ACE_Guard::ACE_Guard (ACE_LOCK &l) + : lock_ (&l), + owner_ (0) +{ + this->acquire (); +} + +template ACE_INLINE +ACE_Guard::ACE_Guard (ACE_LOCK &l, int block) + : lock_ (&l), + owner_ (0) +{ + if (block) + this->acquire (); + else + this->tryacquire (); +} + +template ACE_INLINE +ACE_Guard::ACE_Guard (ACE_LOCK &l, int block, int become_owner) + : lock_ (&l), + owner_ (become_owner == 0 ? -1 : 0) +{ + ACE_UNUSED_ARG (block); +} + +// Implicitly and automatically acquire (or try to acquire) the +// lock. + +template ACE_INLINE +ACE_Guard::~ACE_Guard (void) +{ + this->release (); +} + +template ACE_INLINE int +ACE_Guard::locked (void) const +{ + return this->owner_ != -1; +} + +template ACE_INLINE int +ACE_Guard::remove (void) +{ + return this->lock_->remove (); +} + +template ACE_INLINE void +ACE_Guard::disown (void) +{ + this->owner_ = -1; +} + +template ACE_INLINE +ACE_Write_Guard::ACE_Write_Guard (ACE_LOCK &m) + : ACE_Guard (&m) +{ + this->acquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::acquire_write (void) +{ + return this->owner_ = this->lock_->acquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::acquire (void) +{ + return this->owner_ = this->lock_->acquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::tryacquire_write (void) +{ + return this->owner_ = this->lock_->tryacquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::tryacquire (void) +{ + return this->owner_ = this->lock_->tryacquire_write (); +} + +template ACE_INLINE +ACE_Write_Guard::ACE_Write_Guard (ACE_LOCK &m, + int block) + : ACE_Guard (&m) +{ + if (block) + this->acquire_write (); + else + this->tryacquire_write (); +} + +template ACE_INLINE int +ACE_Read_Guard::acquire_read (void) +{ + return this->owner_ = this->lock_->acquire_read (); +} + +template ACE_INLINE int +ACE_Read_Guard::acquire (void) +{ + return this->owner_ = this->lock_->acquire_read (); +} + +template ACE_INLINE int +ACE_Read_Guard::tryacquire_read (void) +{ + return this->owner_ = this->lock_->tryacquire_read (); +} + +template ACE_INLINE int +ACE_Read_Guard::tryacquire (void) +{ + return this->owner_ = this->lock_->tryacquire_read (); +} + +template ACE_INLINE +ACE_Read_Guard::ACE_Read_Guard (ACE_LOCK &m) + : ACE_Guard (&m) +{ + this->acquire_read (); +} + +template ACE_INLINE +ACE_Read_Guard::ACE_Read_Guard (ACE_LOCK &m, + int block) + : ACE_Guard (&m) +{ + if (block) + this->acquire_read (); + else + this->tryacquire_read (); +} + +template ACE_INLINE +ACE_Lock_Adapter::ACE_Lock_Adapter (ACE_LOCKING_MECHANISM &lock) + : lock_ (&lock), + delete_lock_ (0) +{ +} + +template ACE_INLINE +ACE_Lock_Adapter::~ACE_Lock_Adapter (void) +{ + if (this->delete_lock_) + delete this->lock_; +} + +// Explicitly destroy the lock. +template ACE_INLINE int +ACE_Lock_Adapter::remove (void) +{ + return this->lock_->remove (); +} + +// Block the thread until the lock is acquired. +template ACE_INLINE int +ACE_Lock_Adapter::acquire (void) +{ + return this->lock_->acquire (); +} + +// Conditionally acquire the lock (i.e., won't block). + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire (void) +{ + return this->lock_->tryacquire (); +} + +// Release the lock. + +template ACE_INLINE int +ACE_Lock_Adapter::release (void) +{ + return this->lock_->release (); +} + +// Block until the thread acquires a read lock. If the locking +// mechanism doesn't support read locks then this just calls +// . + +template ACE_INLINE int +ACE_Lock_Adapter::acquire_read (void) +{ + return this->lock_->acquire_read (); +} + +// Block until the thread acquires a write lock. If the locking +// mechanism doesn't support read locks then this just calls +// . + +template ACE_INLINE int +ACE_Lock_Adapter::acquire_write (void) +{ + return this->lock_->acquire_write (); +} + +// Conditionally acquire a read lock. If the locking mechanism +// doesn't support read locks then this just calls . + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire_read (void) +{ + return this->lock_->tryacquire_read (); +} + +// Conditionally acquire a write lock. If the locking mechanism +// doesn't support write locks then this just calls . + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire_write (void) +{ + return this->lock_->tryacquire_write (); +} + +// Conditionally try to upgrade a lock held for read to a write lock. +// If the locking mechanism doesn't support read locks then this just +// calls . Returns 0 on success, -1 on failure. + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire_write_upgrade (void) +{ + return this->lock_->tryacquire_write_upgrade (); +} + +template ACE_INLINE +ACE_Reverse_Lock::ACE_Reverse_Lock (ACE_LOCKING_MECHANISM &lock, + ACE_Acquire_Method::METHOD_TYPE acquire_method) + : lock_ (lock), + acquire_method_ (acquire_method) +{ +} + +// Explicitly destroy the lock. +template ACE_INLINE int +ACE_Reverse_Lock::remove (void) +{ + return this->lock_.remove (); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::acquire (void) +{ + return this->lock_.release (); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Acquire the lock. +template ACE_INLINE int +ACE_Reverse_Lock::release (void) +{ + if (this->acquire_method_ == ACE_Acquire_Method::ACE_READ) + return this->lock_.acquire_read (); + else if (this->acquire_method_ == ACE_Acquire_Method::ACE_WRITE) + return this->lock_.acquire_write (); + else + return this->lock_.acquire (); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::acquire_read (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::acquire_write (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire_read (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire_write (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire_write_upgrade (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +#if defined (ACE_HAS_THREADS) + +template ACE_INLINE int +ACE_Condition::remove (void) +{ + // ACE_TRACE ("ACE_Condition::remove"); + + // cond_destroy() is called in a loop if the condition variable is + // BUSY. This avoids a condition where a condition is signaled and + // because of some timing problem, the thread that is to be signaled + // has called the cond_wait routine after the signal call. Since + // the condition signal is not queued in any way, deadlock occurs. + + int result = 0; + +#if defined (CHORUS) + // Are we the owner? + if (this->process_cond_ && this->condname_) + { + // Only destroy the condition if we're the ones who initialized + // it. + while ((result = ACE_OS::cond_destroy (this->process_cond_)) == -1 + && errno == EBUSY) + { + ACE_OS::cond_broadcast (this->process_cond_); + ACE_OS::thr_yield (); + } + ACE_OS::munmap (this->process_cond_, + sizeof (ACE_cond_t)); + ACE_OS::shm_unlink (this->condname_); + ACE_OS::free (ACE_static_cast (void *, + ACE_const_cast (ACE_TCHAR *, + this->condname_))); + } + else if (this->process_cond_) + { + ACE_OS::munmap (this->process_cond_, + sizeof (ACE_cond_t)); + result = 0; + } + else +#endif /* CHORUS */ + + while ((result = ACE_OS::cond_destroy (&this->cond_)) == -1 + && errno == EBUSY) + { + ACE_OS::cond_broadcast (&this->cond_); + ACE_OS::thr_yield (); + } + + return result; +} + +template ACE_INLINE MUTEX & +ACE_Condition::mutex (void) +{ + // ACE_TRACE ("ACE_Condition::mutex"); + return this->mutex_; +} + +template ACE_INLINE int +ACE_Condition::signal (void) +{ +// ACE_TRACE ("ACE_Condition::signal"); +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_signal (this->process_cond_); +#endif /* CHORUS */ + return ACE_OS::cond_signal (&this->cond_); +} + +template ACE_INLINE int +ACE_Condition::broadcast (void) +{ +// ACE_TRACE ("ACE_Condition::broadcast"); +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_broadcast (this->process_cond_); +#endif /* CHORUS */ + return ACE_OS::cond_broadcast (&this->cond_); +} + +#endif /* ACE_HAS_THREADS */ + +#if !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) +template ACE_INLINE +ACE_TSS::ACE_TSS (TYPE *type) + : type_ (type) +{ +} + +template ACE_INLINE int +ACE_TSS::ts_init (void) const +{ + return 0; +} + +template ACE_INLINE TYPE * +ACE_TSS::ts_object (void) const +{ + return this->type_; +} + +template ACE_INLINE TYPE * +ACE_TSS::ts_object (TYPE *type) +{ + this->type_ = type; + return this->type_; +} + +template ACE_INLINE TYPE * +ACE_TSS::ts_get (void) const +{ + return this->type_; +} + +#endif /* ! (defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) */ diff --git a/ace/Threads/Synch_T.i~ b/ace/Threads/Synch_T.i~ new file mode 100644 index 00000000000..50cd0a710e9 --- /dev/null +++ b/ace/Threads/Synch_T.i~ @@ -0,0 +1,452 @@ +/* -*- C++ -*- */ +// $Id$ + +#include "ace/Thread.h" + +template ACE_INLINE int +ACE_Guard::acquire (void) +{ + return this->owner_ = this->lock_->acquire (); +} + +template ACE_INLINE int +ACE_Guard::tryacquire (void) +{ + return this->owner_ = this->lock_->tryacquire (); +} + +template ACE_INLINE int +ACE_Guard::release (void) +{ + if (this->owner_ == -1) + return -1; + else + { + this->owner_ = -1; + return this->lock_->release (); + } +} + +template ACE_INLINE +ACE_Guard::ACE_Guard (ACE_LOCK &l) + : lock_ (&l), + owner_ (0) +{ + this->acquire (); +} + +template ACE_INLINE +ACE_Guard::ACE_Guard (ACE_LOCK &l, int block) + : lock_ (&l), + owner_ (0) +{ + if (block) + this->acquire (); + else + this->tryacquire (); +} + +template ACE_INLINE +ACE_Guard::ACE_Guard (ACE_LOCK &l, int block, int become_owner) + : lock_ (&l), + owner_ (become_owner == 0 ? -1 : 0) +{ + ACE_UNUSED_ARG (block); +} + +// Implicitly and automatically acquire (or try to acquire) the +// lock. + +template ACE_INLINE +ACE_Guard::~ACE_Guard (void) +{ + this->release (); +} + +template ACE_INLINE int +ACE_Guard::locked (void) const +{ + return this->owner_ != -1; +} + +template ACE_INLINE int +ACE_Guard::remove (void) +{ + return this->lock_->remove (); +} + +template ACE_INLINE void +ACE_Guard::disown (void) +{ + this->owner_ = -1; +} + +template ACE_INLINE +ACE_Write_Guard::ACE_Write_Guard (ACE_LOCK &m) + : ACE_Guard (&m) +{ + this->acquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::acquire_write (void) +{ + return this->owner_ = this->lock_->acquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::acquire (void) +{ + return this->owner_ = this->lock_->acquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::tryacquire_write (void) +{ + return this->owner_ = this->lock_->tryacquire_write (); +} + +template ACE_INLINE int +ACE_Write_Guard::tryacquire (void) +{ + return this->owner_ = this->lock_->tryacquire_write (); +} + +template ACE_INLINE +ACE_Write_Guard::ACE_Write_Guard (ACE_LOCK &m, + int block) + : ACE_Guard (&m) +{ + if (block) + this->acquire_write (); + else + this->tryacquire_write (); +} + +template ACE_INLINE int +ACE_Read_Guard::acquire_read (void) +{ + return this->owner_ = this->lock_->acquire_read (); +} + +template ACE_INLINE int +ACE_Read_Guard::acquire (void) +{ + return this->owner_ = this->lock_->acquire_read (); +} + +template ACE_INLINE int +ACE_Read_Guard::tryacquire_read (void) +{ + return this->owner_ = this->lock_->tryacquire_read (); +} + +template ACE_INLINE int +ACE_Read_Guard::tryacquire (void) +{ + return this->owner_ = this->lock_->tryacquire_read (); +} + +template ACE_INLINE +ACE_Read_Guard::ACE_Read_Guard (ACE_LOCK &m) + : ACE_Guard (&m) +{ + this->acquire_read (); +} + +template ACE_INLINE +ACE_Read_Guard::ACE_Read_Guard (ACE_LOCK &m, + int block) + : ACE_Guard (&m) +{ + if (block) + this->acquire_read (); + else + this->tryacquire_read (); +} + +template ACE_INLINE +ACE_Lock_Adapter::ACE_Lock_Adapter (ACE_LOCKING_MECHANISM &lock) + : lock_ (&lock), + delete_lock_ (0) +{ +} + +template ACE_INLINE +ACE_Lock_Adapter::~ACE_Lock_Adapter (void) +{ + if (this->delete_lock_) + delete this->lock_; +} + +// Explicitly destroy the lock. +template ACE_INLINE int +ACE_Lock_Adapter::remove (void) +{ + return this->lock_->remove (); +} + +// Block the thread until the lock is acquired. +template ACE_INLINE int +ACE_Lock_Adapter::acquire (void) +{ + return this->lock_->acquire (); +} + +// Conditionally acquire the lock (i.e., won't block). + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire (void) +{ + return this->lock_->tryacquire (); +} + +// Release the lock. + +template ACE_INLINE int +ACE_Lock_Adapter::release (void) +{ + return this->lock_->release (); +} + +// Block until the thread acquires a read lock. If the locking +// mechanism doesn't support read locks then this just calls +// . + +template ACE_INLINE int +ACE_Lock_Adapter::acquire_read (void) +{ + return this->lock_->acquire_read (); +} + +// Block until the thread acquires a write lock. If the locking +// mechanism doesn't support read locks then this just calls +// . + +template ACE_INLINE int +ACE_Lock_Adapter::acquire_write (void) +{ + return this->lock_->acquire_write (); +} + +// Conditionally acquire a read lock. If the locking mechanism +// doesn't support read locks then this just calls . + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire_read (void) +{ + return this->lock_->tryacquire_read (); +} + +// Conditionally acquire a write lock. If the locking mechanism +// doesn't support write locks then this just calls . + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire_write (void) +{ + return this->lock_->tryacquire_write (); +} + +// Conditionally try to upgrade a lock held for read to a write lock. +// If the locking mechanism doesn't support read locks then this just +// calls . Returns 0 on success, -1 on failure. + +template ACE_INLINE int +ACE_Lock_Adapter::tryacquire_write_upgrade (void) +{ + return this->lock_->tryacquire_write_upgrade (); +} + +template ACE_INLINE +ACE_Reverse_Lock::ACE_Reverse_Lock (ACE_LOCKING_MECHANISM &lock, + ACE_Acquire_Method::METHOD_TYPE acquire_method) + : lock_ (lock), + acquire_method_ (acquire_method) +{ +} + +// Explicitly destroy the lock. +template ACE_INLINE int +ACE_Reverse_Lock::remove (void) +{ + return this->lock_.remove (); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::acquire (void) +{ + return this->lock_.release (); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Acquire the lock. +template ACE_INLINE int +ACE_Reverse_Lock::release (void) +{ + if (this->acquire_method_ == ACE_Acquire_Method::ACE_READ) + return this->lock_.acquire_read (); + else if (this->acquire_method_ == ACE_Acquire_Method::ACE_WRITE) + return this->lock_.acquire_write (); + else + return this->lock_.acquire (); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::acquire_read (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::acquire_write (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire_read (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire_write (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +// Release the lock. +template ACE_INLINE int +ACE_Reverse_Lock::tryacquire_write_upgrade (void) +{ + ACE_NOTSUP_RETURN (-1); +} + +#if defined (ACE_HAS_THREADS) + +template ACE_INLINE int +ACE_Condition::remove (void) +{ + // ACE_TRACE ("ACE_Condition::remove"); + + // cond_destroy() is called in a loop if the condition variable is + // BUSY. This avoids a condition where a condition is signaled and + // because of some timing problem, the thread that is to be signaled + // has called the cond_wait routine after the signal call. Since + // the condition signal is not queued in any way, deadlock occurs. + + int result = 0; + +#if defined (CHORUS) + // Are we the owner? + if (this->process_cond_ && this->condname_) + { + // Only destroy the condition if we're the ones who initialized + // it. + while ((result = ACE_OS::cond_destroy (this->process_cond_)) == -1 + && errno == EBUSY) + { + ACE_OS::cond_broadcast (this->process_cond_); + ACE_OS::thr_yield (); + } + ACE_OS::munmap (this->process_cond_, + sizeof (ACE_cond_t)); + ACE_OS::shm_unlink (this->condname_); + ACE_OS::free (ACE_static_cast (void *, + ACE_const_cast (ACE_TCHAR *, + this->condname_))); + } + else if (this->process_cond_) + { + ACE_OS::munmap (this->process_cond_, + sizeof (ACE_cond_t)); + result = 0; + } + else +#endif /* CHORUS */ + + while ((result = ACE_OS::cond_destroy (&this->cond_)) == -1 + && errno == EBUSY) + { + ACE_OS::cond_broadcast (&this->cond_); + ACE_OS::thr_yield (); + } + + return result; +} + +template ACE_INLINE MUTEX & +ACE_Condition::mutex (void) +{ + // ACE_TRACE ("ACE_Condition::mutex"); + return this->mutex_; +} + +template ACE_INLINE int +ACE_Condition::signal (void) +{ +// ACE_TRACE ("ACE_Condition::signal"); +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_signal (this->process_cond_); +#endif /* CHORUS */ + return ACE_OS::cond_signal (&this->cond_); +} + +template ACE_INLINE int +ACE_Condition::broadcast (void) +{ +// ACE_TRACE ("ACE_Condition::broadcast"); +#if defined (CHORUS) + if (this->process_cond_ != 0) + return ACE_OS::cond_broadcast (this->process_cond_); +#endif /* CHORUS */ + return ACE_OS::cond_broadcast (&this->cond_); +} + +#endif /* ACE_HAS_THREADS */ + +#if !(defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) +template ACE_INLINE +ACE_TSS::ACE_TSS (TYPE *type) + : type_ (type) +{ +} + +template ACE_INLINE int +ACE_TSS::ts_init (void) const +{ + return 0; +} + +template ACE_INLINE TYPE * +ACE_TSS::ts_object (void) const +{ + return this->type_; +} + +template ACE_INLINE TYPE * +ACE_TSS::ts_object (TYPE *type) +{ + this->type_ = type; + return this->type_; +} + +template ACE_INLINE TYPE * +ACE_TSS::ts_get (void) const +{ + return this->type_; +} + +#endif /* ! (defined (ACE_HAS_THREADS) && (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION))) */ diff --git a/ace/Threads/Thread.cpp b/ace/Threads/Thread.cpp new file mode 100644 index 00000000000..7fa156c2488 --- /dev/null +++ b/ace/Threads/Thread.cpp @@ -0,0 +1,90 @@ +// Thread.cpp +// $Id$ + +#include "ace/Thread.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Thread.i" +#endif /* !defined (__ACE_INLINE__) */ + +ACE_RCSID(ace, Thread, "$Id$") + +#if defined (ACE_HAS_THREADS) + +int +ACE_Thread::spawn_n (size_t n, + ACE_THR_FUNC func, + void *arg, + long flags, + long priority, + void *stack[], + size_t stack_size[], + ACE_Thread_Adapter *thread_adapter) +{ + ACE_TRACE ("ACE_Thread::spawn_n"); + ACE_thread_t t_id; + size_t i; + + for (i = 0; i < n; i++) + // Bail out if error occurs. + if (ACE_OS::thr_create (func, + arg, + flags, + &t_id, + 0, + priority, + stack == 0 ? 0 : stack[i], + stack_size == 0 ? 0 : stack_size[i], + thread_adapter) != 0) + break; + + return i; +} + +int +ACE_Thread::spawn_n (ACE_thread_t thread_ids[], + size_t n, + ACE_THR_FUNC func, + void *arg, + long flags, + long priority, + void *stack[], + size_t stack_size[], + ACE_hthread_t thread_handles[], + ACE_Thread_Adapter *thread_adapter) +{ + ACE_TRACE ("ACE_Thread::spawn_n"); + size_t i; + + for (i = 0; i < n; i++) + { + ACE_thread_t t_id; + ACE_hthread_t t_handle; + + int result = + ACE_OS::thr_create (func, + arg, + flags, + &t_id, + &t_handle, + priority, + stack == 0 ? 0 : stack[i], + stack_size == 0 ? 0 : stack_size[i], + thread_adapter); + + if (result == 0) + { + if (thread_ids != 0) + thread_ids[i] = t_id; + if (thread_handles != 0) + thread_handles[i] = t_handle; + } + else + // Bail out if error occurs. + break; + } + + return i; +} + +#endif /* ACE_HAS_THREADS */ diff --git a/ace/Threads/Thread.h b/ace/Threads/Thread.h new file mode 100644 index 00000000000..9de66e1d3fe --- /dev/null +++ b/ace/Threads/Thread.h @@ -0,0 +1,240 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Thread.h + * + * $Id$ + * + * @author Douglas Schmidt + */ +//============================================================================= + +#ifndef ACE_THREAD_H +#define ACE_THREAD_H +#include "ace/pre.h" + +#include "ace/ACE.h" +#include "ace/Thread_Adapter.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_Thread + * + * @brief Provides a wrapper for threads. + * + * This class provides a common interface that is mapped onto + * POSIX Pthreads, Solaris threads, Win32 threads, VxWorks + * threads, or pSoS threads. Note, however, that it is + * generally a better idea to use the + * programming API rather than the API since the + * thread manager is more powerful. + */ +class ACE_Export ACE_Thread +{ +public: + /** + * Creates a new thread having attributes and running + * with (if is non-0 then and + * are ignored and are obtained from ). + * and are set to the thread's ID and handle (?), + * respectively. The thread runs at priority (see + * below). + * + * The are a bitwise-OR of the following: + * = BEGIN + * THR_CANCEL_DISABLE, THR_CANCEL_ENABLE, THR_CANCEL_DEFERRED, + * THR_CANCEL_ASYNCHRONOUS, THR_BOUND, THR_NEW_LWP, THR_DETACHED, + * THR_SUSPENDED, THR_DAEMON, THR_JOINABLE, THR_SCHED_FIFO, + * THR_SCHED_RR, THR_SCHED_DEFAULT, THR_EXPLICIT_SCHED, + * THR_SCOPE_SYSTEM, THR_SCOPE_PROCESS + * = END + * + * By default, or if is set to + * ACE_DEFAULT_THREAD_PRIORITY, an "appropriate" priority value for + * the given scheduling policy (specified in , e.g., + * ) is used. This value is calculated + * dynamically, and is the median value between the minimum and + * maximum priority values for the given policy. If an explicit + * value is given, it is used. Note that actual priority values are + * EXTREMEMLY implementation-dependent, and are probably best + * avoided. + * + * Note that is always deleted when + * is called, so it must be allocated with global operator new. + */ + static int spawn (ACE_THR_FUNC func, + void *arg = 0, + long flags = THR_NEW_LWP | THR_JOINABLE, + ACE_thread_t *t_id = 0, + ACE_hthread_t *t_handle = 0, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + void *stack = 0, + size_t stack_size = 0, + ACE_Thread_Adapter *thread_adapter = 0); + + /** + * Spawn N new threads, which execute with argument (if + * is non-0 then and are ignored and + * are obtained from ). If != 0 it is + * assumed to be an array of pointers to the base of the stacks + * to use for the threads being spawned. Likewise, if + * != 0 it is assumed to be an array of values indicating how + * big each of the corresponding s are. Returns the number + * of threads actually spawned (if this doesn't equal the number + * requested then something has gone wrong and will + * explain...). + * + * See also . + */ + static int spawn_n (size_t n, + ACE_THR_FUNC func, + void *arg = 0, + long flags = THR_NEW_LWP | THR_JOINABLE, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + void *stack[] = 0, + size_t stack_size[] = 0, + ACE_Thread_Adapter *thread_adapter = 0); + + /** + * Spawn new threads, which execute with argument + * (if is non-0 then and are ignored + * and are obtained from ). The thread_ids of + * successfully spawned threads will be placed into the + * buffer (which must be the same size as ). If != 0 it + * is assumed to be an array of pointers to the base of the + * stacks to use for the threads being spawned. If != + * 0 it is assumed to be an array of values indicating how big + * each of the corresponding s are. If != 0 + * it is assumed to be an array of thread_handles that will be + * assigned the values of the thread handles being spawned. Returns + * the number of threads actually spawned (if this doesn't equal the + * number requested then something has gone wrong and will + * explain...). + * + * See also . + */ + static int spawn_n (ACE_thread_t thread_ids[], + size_t n, + ACE_THR_FUNC func, + void *arg, + long flags, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + void *stack[] = 0, + size_t stack_size[] = 0, + ACE_hthread_t thread_handles[] = 0, + ACE_Thread_Adapter *thread_adapter = 0); + + /// Wait for one or more threads to exit and reap their exit status. + static int join (ACE_thread_t, + ACE_thread_t *, + void **status); + + /// Wait for one thread to exit and reap its exit status. + static int join (ACE_hthread_t, + void ** = 0); + + /// Continue the execution of a previously suspended thread. + static int resume (ACE_hthread_t); + + /// Suspend the execution of a particular thread. + static int suspend (ACE_hthread_t); + + /// Get the priority of a particular thread. + static int getprio (ACE_hthread_t, int &prio); + + /// Set the priority of a particular thread. + static int setprio (ACE_hthread_t, int prio); + + /// Send a signal to the thread. + static int kill (ACE_thread_t, int signum); + + /// Yield the thread to another. + static void yield (void); + + /** + * Return the unique kernel handle of the thread. Note that on + * Win32 this is actually a pseudohandle, which cannot be shared + * with other processes or waited on by threads. To locate the real + * handle, please use the method. + */ + static void self (ACE_hthread_t &t_handle); + + /// Return the unique ID of the thread. + static ACE_thread_t self (void); + + /// Exit the current thread and return "status". + /// Should _not_ be called by main thread. + static void exit (void *status = 0); + + /// Get the LWP concurrency level of the process. + static int getconcurrency (void); + + /// Set the LWP concurrency level of the process. + static int setconcurrency (int new_level); + + /// Change and/or examine calling thread's signal mask. + static int sigsetmask (int how, + const sigset_t *sigset, + sigset_t *osigset = 0); + + static int keycreate (ACE_thread_key_t *keyp, +#if defined (ACE_HAS_THR_C_DEST) + ACE_THR_C_DEST destructor, +#else + ACE_THR_DEST destructor, +#endif /* ACE_HAS_THR_C_DEST */ + /** + * Allocates a that is used to identify data that is specific + * to each thread in the process. The key is global to all threads + * in the process. + */ + void * = 0); + + /// Free up the key so that other threads can reuse it. + static int keyfree (ACE_thread_key_t key); + + /// Bind value to the thread-specific data key, , for the calling + /// thread. + static int setspecific (ACE_thread_key_t key, + void *value); + + /// Stores the current value bound to for the calling thread + /// into the location pointed to by . + static int getspecific (ACE_thread_key_t key, + void **valuep); + + /// Disable thread cancellation. + static int disablecancel (struct cancel_state *old_state); + + /// Enable thread cancellation. + static int enablecancel (struct cancel_state *old_state, + int flag); + + /// Set the cancellation state. + static int setcancelstate (struct cancel_state &new_state, + struct cancel_state *old_state); + + /** + * Cancel a thread. Note that this method is only portable on + * platforms, such as POSIX pthreads, that support thread + * cancellation. + */ + static int cancel (ACE_thread_t t_id); + + /// Test the cancel. + static void testcancel (void); + +private: + /// Ensure that we don't get instantiated. + ACE_Thread (void); +}; + +#if defined (__ACE_INLINE__) +#include "ace/Thread.i" +#endif /* __ACE_INLINE__ */ +#include "ace/post.h" +#endif /* ACE_THREAD_H */ diff --git a/ace/Threads/Thread.i b/ace/Threads/Thread.i new file mode 100644 index 00000000000..610b84f1ad0 --- /dev/null +++ b/ace/Threads/Thread.i @@ -0,0 +1,272 @@ +/* -*- C++ -*- */ +// $Id$ + +// Thread.i + +// Allocates a that is used to identify data that is specific +// to each thread in the process. The key is global to all threads in +// the process. + +ACE_INLINE int +ACE_Thread::keycreate (ACE_thread_key_t *keyp, +#if defined (ACE_HAS_THR_C_DEST) + ACE_THR_C_DEST destructor, +#else + ACE_THR_DEST destructor, +#endif /* ACE_HAS_THR_C_DEST */ + void *inst) +{ + // ACE_TRACE ("ACE_Thread::keycreate"); + return ACE_OS::thr_keycreate (keyp, destructor, inst); +} + +// Free up the key so that other threads can reuse it. + +ACE_INLINE int +ACE_Thread::keyfree (ACE_thread_key_t key) +{ + ACE_TRACE ("ACE_Thread::keyfree"); + return ACE_OS::thr_keyfree (key); +} + +// Bind value to the thread-specific data key, , for the calling +// thread. + +ACE_INLINE int +ACE_Thread::setspecific (ACE_thread_key_t key, void *value) +{ + // ACE_TRACE ("ACE_Thread::setspecific"); + return ACE_OS::thr_setspecific (key, value); +} + +// Stores the current value bound to for the calling thread +// into the location pointed to by . + +ACE_INLINE int +ACE_Thread::getspecific (ACE_thread_key_t key, void **valuep) +{ + // ACE_TRACE ("ACE_Thread::getspecific"); + return ACE_OS::thr_getspecific (key, valuep); +} + +ACE_INLINE ACE_thread_t +ACE_Thread::self (void) +{ +// ACE_TRACE ("ACE_Thread::self"); + return ACE_OS::thr_self (); +} + +ACE_INLINE void +ACE_Thread::exit (void *status) +{ + ACE_TRACE ("ACE_Thread::exit"); + ACE_OS::thr_exit (status); +} + +ACE_INLINE void +ACE_Thread::yield (void) +{ + ACE_TRACE ("ACE_Thread::yield"); + ACE_OS::thr_yield (); +} + +ACE_INLINE int +ACE_Thread::spawn (ACE_THR_FUNC func, + void *arg, + long flags, + ACE_thread_t *t_id, + ACE_hthread_t *t_handle, + long priority, + void *thr_stack, + size_t thr_stack_size, + ACE_Thread_Adapter *thread_adapter) +{ + ACE_TRACE ("ACE_Thread::spawn"); + + return ACE_OS::thr_create (func, + arg, + flags, + t_id, + t_handle, + priority, + thr_stack, + thr_stack_size, + thread_adapter); +} + +ACE_INLINE int +ACE_Thread::resume (ACE_hthread_t t_id) +{ + ACE_TRACE ("ACE_Thread::resume"); + return ACE_OS::thr_continue (t_id); +} + +ACE_INLINE int +ACE_Thread::suspend (ACE_hthread_t t_id) +{ + ACE_TRACE ("ACE_Thread::suspend"); + return ACE_OS::thr_suspend (t_id); +} + +ACE_INLINE int +ACE_Thread::kill (ACE_thread_t t_id, int signum) +{ + ACE_TRACE ("ACE_Thread::kill"); + return ACE_OS::thr_kill (t_id, signum); +} + +ACE_INLINE int +ACE_Thread::join (ACE_thread_t wait_for, + ACE_thread_t *departed, + void **status) +{ + ACE_TRACE ("ACE_Thread::join"); + return ACE_OS::thr_join (wait_for, departed, status); +} + +ACE_INLINE int +ACE_Thread::join (ACE_hthread_t wait_for, + void **status) +{ + ACE_TRACE ("ACE_Thread::join"); + return ACE_OS::thr_join (wait_for, status); +} + +ACE_INLINE int +ACE_Thread::getconcurrency (void) +{ + ACE_TRACE ("ACE_Thread::getconcurrency"); + return ACE_OS::thr_getconcurrency (); +} + +ACE_INLINE int +ACE_Thread::setconcurrency (int new_level) +{ + ACE_TRACE ("ACE_Thread::setconcurrency"); + return ACE_OS::thr_setconcurrency (new_level); +} + +ACE_INLINE int +ACE_Thread::sigsetmask (int how, + const sigset_t *sigset, + sigset_t *osigset) +{ + ACE_TRACE ("ACE_Thread::sigsetmask"); + return ACE_OS::thr_sigsetmask (how, sigset, osigset); +} + +ACE_INLINE int +ACE_Thread::disablecancel (struct cancel_state *old_state) +{ + ACE_TRACE ("ACE_Thread::disablecancel"); + int old_cstate = 0; + int result = ACE_OS::thr_setcancelstate (THR_CANCEL_DISABLE, + &old_cstate); + if (result == 0 && old_state != 0) + { + ACE_OS::memset (old_state, + 0, + sizeof (old_state)); + old_state->cancelstate = old_cstate; + } + + return result; +} + +ACE_INLINE int +ACE_Thread::enablecancel (struct cancel_state *old_state, + int flag) +{ + ACE_TRACE ("ACE_Thread::enablecancel"); + int old_cstate = 0; + int old_ctype = 0; + int result; + + result = ACE_OS::thr_setcancelstate (THR_CANCEL_ENABLE, + &old_cstate); + if (result != 0) + return result; + + result = ACE_OS::thr_setcanceltype (flag, + &old_ctype); + if (result != 0) + return result; + + if (old_state != 0) + { + old_state->cancelstate = old_cstate; + old_state->canceltype = old_ctype; + } + + return 0; +} + +ACE_INLINE int +ACE_Thread::setcancelstate (struct cancel_state &new_state, + struct cancel_state *old_state) +{ + ACE_TRACE ("ACE_Thread::setcancelstate"); + int old_cstate = 0; + int old_ctype = 0; + + if (new_state.cancelstate != 0 + && ACE_OS::thr_setcancelstate (new_state.cancelstate, + &old_cstate) != 0) + return -1; + + if (new_state.canceltype != 0 + && ACE_OS::thr_setcanceltype (new_state.canceltype, + &old_ctype) != 0) + { + int o_cstate; + + ACE_OS::thr_setcancelstate (old_cstate, + &o_cstate); + return -1; + } + + if (old_state != 0) + { + old_state->cancelstate = old_cstate; + old_state->canceltype = old_ctype; + } + + return 0; +} + +ACE_INLINE int +ACE_Thread::cancel (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread::cancel"); + + return ACE_OS::thr_cancel (t_id); +} + +ACE_INLINE void +ACE_Thread::testcancel (void) +{ + ACE_TRACE ("ACE_Thread::testcancel"); + + ACE_OS::thr_testcancel (); +} + +ACE_INLINE void +ACE_Thread::self (ACE_hthread_t &t_id) +{ +// ACE_TRACE ("ACE_Thread::self"); + ACE_OS::thr_self (t_id); +} + +ACE_INLINE int +ACE_Thread::getprio (ACE_hthread_t t_id, int &prio) +{ + ACE_TRACE ("ACE_Thread::getprio"); + return ACE_OS::thr_getprio (t_id, prio); +} + +ACE_INLINE int +ACE_Thread::setprio (ACE_hthread_t t_id, int prio) +{ + ACE_TRACE ("ACE_Thread::setprio"); + return ACE_OS::thr_setprio (t_id, prio); +} diff --git a/ace/Threads/Thread_Adapter.cpp b/ace/Threads/Thread_Adapter.cpp new file mode 100644 index 00000000000..91357bfc58f --- /dev/null +++ b/ace/Threads/Thread_Adapter.cpp @@ -0,0 +1,252 @@ +// $Id$ + +#include "ace/Thread_Adapter.h" +#include "ace/OS.h" +#include "ace/Thread_Manager.h" +#include "ace/Thread_Exit.h" +#include "ace/Thread_Hook.h" + +ACE_RCSID(ace, Thread_Adapter, "$Id$") + +#if !defined (ACE_HAS_INLINED_OSCALLS) +# include "ace/Thread_Adapter.inl" +#endif /* ACE_HAS_INLINED_OS_CALLS */ + +ACE_Thread_Adapter::ACE_Thread_Adapter (ACE_THR_FUNC user_func, + void *arg, + ACE_THR_C_FUNC entry_point, + ACE_Thread_Manager *tm, + ACE_Thread_Descriptor *td +#if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS) + , ACE_SEH_EXCEPT_HANDLER selector, + ACE_SEH_EXCEPT_HANDLER handler +#endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */ + ) + : ACE_Base_Thread_Adapter ( + user_func + , arg + , entry_point + , td +#if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS) + , selector + , handler +#endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */ + ) + , thr_mgr_ (tm) +{ + ACE_OS_TRACE ("ACE_Thread_Adapter::ACE_Thread_Adapter"); +} + +ACE_Thread_Adapter::~ACE_Thread_Adapter (void) +{ +} + +void * +ACE_Thread_Adapter::invoke (void) +{ + // Inherit the logging features if the parent thread has an + // ACE_Log_Msg instance in thread-specific storage. + this->inherit_log_msg (); + +#if !defined(ACE_USE_THREAD_MANAGER_ADAPTER) + // NOTE: this preprocessor directive should match the one in above + // ACE_Thread_Exit::instance (). With the Xavier Pthreads package, + // the exit_hook in TSS causes a seg fault. So, this works around + // that by creating exit_hook on the stack. +# if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION) + // Obtain our thread-specific exit hook and make sure that it knows + // how to clean us up! Note that we never use this pointer directly + // (it's stored in thread-specific storage), so it's ok to + // dereference it here and only store it as a reference. + + // Except if it is null, then the thr_mgr() method crashes. + // -jxh + + ACE_Thread_Exit *exit_hook_instance = ACE_Thread_Exit::instance (); + ACE_Thread_Exit_Maybe exit_hook_maybe (exit_hook_instance == 0); + ACE_Thread_Exit *exit_hook_ptr = exit_hook_instance + ? exit_hook_instance + : exit_hook_maybe.instance (); + ACE_Thread_Exit &exit_hook = *exit_hook_ptr; + + if (this->thr_mgr () != 0) + { + // Keep track of the that's associated with this + // . + exit_hook.thr_mgr (this->thr_mgr ()); + } +# else + // Without TSS, create an instance. When this + // function returns, its destructor will be called because the + // object goes out of scope. The drawback with this appraoch is + // that the destructor _won't_ get called if is called. + // So, threads shouldn't exit that way. Instead, they should return + // from . + ACE_Thread_Exit exit_hook; + exit_hook.thr_mgr (this->thr_mgr ()); +# endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE || ACE_HAS_TSS_EMULATION */ + +#endif /* ! ACE_USE_THREAD_MANAGER_ADAPTER */ + + return this->invoke_i (); +} + +void * +ACE_Thread_Adapter::invoke_i (void) +{ + // Extract the arguments. + ACE_THR_FUNC_INTERNAL func = ACE_reinterpret_cast (ACE_THR_FUNC_INTERNAL, + this->user_func_); + void *arg = this->arg_; + +#if defined (ACE_WIN32) && defined (ACE_HAS_MFC) && (ACE_HAS_MFC != 0) + ACE_OS_Thread_Descriptor *thr_desc = this->thr_desc_; +#endif /* ACE_WIN32 && ACE_HAS_MFC && (ACE_HAS_MFC != 0) */ + + // Delete ourselves since we don't need anymore. Make sure + // not to access anywhere below this point. + delete this; + +#if defined (ACE_NEEDS_LWP_PRIO_SET) + // On SunOS, the LWP priority needs to be set in order to get + // preemption when running in the RT class. This is the ACE way to + // do that . . . + ACE_hthread_t thr_handle; + ACE_OS::thr_self (thr_handle); + int prio; + + // thr_getprio () on the current thread should never fail. + ACE_OS::thr_getprio (thr_handle, prio); + + // ACE_OS::thr_setprio () has the special logic to set the LWP priority, + // if running in the RT class. + ACE_OS::thr_setprio (prio); + +#endif /* ACE_NEEDS_LWP_PRIO_SET */ + + void *status = 0; + + ACE_SEH_TRY + { + ACE_SEH_TRY + { + ACE_Thread_Hook *hook = + ACE_OS_Object_Manager::thread_hook (); + + if (hook) + // Invoke the start hook to give the user a chance to + // perform some initialization processing before the + // is invoked. + status = hook->start (ACE_reinterpret_cast (ACE_THR_FUNC, func), + arg); + else + { + // Call thread entry point. +#if defined (ACE_PSOS) + (*func) (arg); +#else /* ! ACE_PSOS */ + status = ACE_reinterpret_cast (void *, (*func) (arg)); +#endif /* ACE_PSOS */ + } +#if defined (ACE_PSOS) + // pSOS task functions do not return a value. + status = 0; +#endif /* ACE_PSOS */ + } + +#if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS) + ACE_SEH_EXCEPT (ACE_OS_Object_Manager::seh_except_selector ()( + (void *) GetExceptionInformation ())) + { + ACE_OS_Object_Manager::seh_except_handler ()(0); + } +#endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */ + } + + ACE_SEH_FINALLY + { + // If we changed this to 1, change the respective if in + // Task::svc_run to 0. +#if 0 + // Call the close> hook. + if (func == ACE_reinterpret_cast (ACE_THR_FUNC_INTERNAL, + ACE_Task_Base::svc_run)) + { + ACE_Task_Base *task_ptr = (ACE_Task_Base *) arg; + ACE_Thread_Manager *thr_mgr_ptr = task_ptr->thr_mgr (); + + // This calls the Task->close () hook. + task_ptr->cleanup (task_ptr, 0); + + // This prevents a second invocation of the cleanup code + // (called later by . + thr_mgr_ptr->at_exit (task_ptr, 0, 0); + } +#endif /* 0 */ + +#if defined (ACE_WIN32) || defined (ACE_HAS_TSS_EMULATION) +# if defined (ACE_WIN32) && defined (ACE_HAS_MFC) && (ACE_HAS_MFC != 0) + int using_afx = -1; + if (thr_desc) + using_afx = ACE_BIT_ENABLED (thr_desc->flags (), THR_USE_AFX); +# endif /* ACE_WIN32 && ACE_HAS_MFC && (ACE_HAS_MFC != 0) */ + // Call TSS destructors. + ACE_OS::cleanup_tss (0 /* not main thread */); + +# if defined (ACE_WIN32) + // Exit the thread. Allow CWinThread-destructor to be invoked + // from AfxEndThread. _endthreadex will be called from + // AfxEndThread so don't exit the thread now if we are running + // an MFC thread. +# if defined (ACE_HAS_MFC) && (ACE_HAS_MFC != 0) + if (using_afx != -1) + { + if (using_afx) + ::AfxEndThread ((DWORD) status); + else + ACE_ENDTHREADEX (status); + } + else + { + // Not spawned by ACE_Thread_Manager, use the old buggy + // version. You should seriously consider using + // ACE_Thread_Manager to spawn threads. The following code + // is know to cause some problem. + CWinThread *pThread = ::AfxGetThread (); + + if (!pThread || pThread->m_nThreadID != ACE_OS::thr_self ()) + ACE_ENDTHREADEX (status); + else + ::AfxEndThread ((DWORD)status); + } +# else + + ACE_ENDTHREADEX (status); +# endif /* ACE_HAS_MFC && ACE_HAS_MFS != 0*/ +# endif /* ACE_WIN32 */ +#endif /* ACE_WIN32 || ACE_HAS_TSS_EMULATION */ + +#if defined (ACE_PSOS) + // This sequence of calls is documented by ISI as the proper way to + // clean up a pSOS task. They affect different components, so only + // try the ones for components that are built with ACE. +# if defined (SC_PREPC) && (SC_PREPC == YES) + ::fclose (0); // Return pREPC+ resources +# endif /* SC_PREPC */ +# if defined (SC_PHILE) && (SC_PHILE == YES) + ::close_f (0); // Return pHILE+ resources +# endif /* SC_PHILE */ +# if defined (SC_PNA) && (SC_PNA == YES) + ::close (0); // Return pNA+ resources +# endif /* SC_PNA */ +# if defined (SC_SC_PREPC) && (SC_PREPC == YES) + ::free (-1); // Return pREPC+ memory +# endif /* SC_PREPC */ + status = ::t_delete (0); // Suicide - only returns on error +#endif /* ACE_PSOS */ + + return status; + } + + ACE_NOTREACHED (return status); +} diff --git a/ace/Threads/Thread_Adapter.h b/ace/Threads/Thread_Adapter.h new file mode 100644 index 00000000000..d7e8bd54f0f --- /dev/null +++ b/ace/Threads/Thread_Adapter.h @@ -0,0 +1,92 @@ + +//============================================================================= +/** + * @file Thread_Adapter.h + * + * $Id$ + * + * @author Carlos O'Ryan + */ +//============================================================================= + +#ifndef ACE_THREAD_ADAPTER_H +#define ACE_THREAD_ADAPTER_H +#include "ace/pre.h" + +#include "ace/Base_Thread_Adapter.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward decl. +class ACE_Thread_Manager; +class ACE_Thread_Descriptor; + +/** + * @class ACE_Thread_Adapter + * + * @brief Converts a C++ function into a function + * function that can be called from a thread creation routine + * (e.g., or <_beginthreadex>) that expects an + * extern "C" entry point. This class also makes it possible to + * transparently provide hooks to register a thread with an + * . + * + * This class is used in . In general, the + * thread that creates an object of this class is different from + * the thread that calls on this object. Therefore, + * the method is responsible for deleting itself. + */ +class ACE_Export ACE_Thread_Adapter : public ACE_Base_Thread_Adapter +{ +public: + ACE_Thread_Adapter (ACE_THR_FUNC user_func, + void *arg, + ACE_THR_C_FUNC entry_point = (ACE_THR_C_FUNC) ace_thread_adapter, + ACE_Thread_Manager *thr_mgr = 0, + ACE_Thread_Descriptor *td = 0 +# if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS) + , ACE_SEH_EXCEPT_HANDLER selector = 0, + ACE_SEH_EXCEPT_HANDLER handler = 0 +# endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */ + /// Constructor. + ); + + /** + * Execute the with the . This function deletes + * , thereby rendering the object useless after the call + * returns. + */ + virtual void *invoke (void); + + /// Accessor for the optional . + ACE_Thread_Manager *thr_mgr (void); + +private: + /// Ensure that this object must be allocated on the heap. + ~ACE_Thread_Adapter (void); + + /// Called by invoke, mainly here to separate the SEH stuff because + /// SEH on Win32 doesn't compile with local vars with destructors. + virtual void *invoke_i (void); + +private: + /// Optional thread manager. + ACE_Thread_Manager *thr_mgr_; + + /// Friend declaration to avoid compiler warning: only defines a private + /// destructor and has no friends. + friend class ACE_Thread_Adapter_Has_Private_Destructor; +}; + +# if defined (ACE_HAS_INLINED_OSCALLS) +# if defined (ACE_INLINE) +# undef ACE_INLINE +# endif /* ACE_INLINE */ +# define ACE_INLINE inline +# include "ace/Thread_Adapter.inl" +# endif /* ACE_HAS_INLINED_OSCALLS */ + +#include "ace/post.h" +#endif /* ACE_THREAD_ADAPTER_H */ diff --git a/ace/Threads/Thread_Adapter.inl b/ace/Threads/Thread_Adapter.inl new file mode 100644 index 00000000000..53855b300cc --- /dev/null +++ b/ace/Threads/Thread_Adapter.inl @@ -0,0 +1,7 @@ +// $Id$ + +ACE_INLINE ACE_Thread_Manager * +ACE_Thread_Adapter::thr_mgr (void) +{ + return this->thr_mgr_; +} diff --git a/ace/Threads/Thread_Control.cpp b/ace/Threads/Thread_Control.cpp new file mode 100644 index 00000000000..e5fff02d507 --- /dev/null +++ b/ace/Threads/Thread_Control.cpp @@ -0,0 +1,90 @@ +// $Id$ + +// +#include "ace/config-all.h" +#if defined (ACE_LEGACY_MODE) +// This silly include breaks a cycle when compiling in backwards +// compatibility mode +# include "ace/Thread_Exit.h" +#endif /* ACE_LEGACY_MODE */ +// + +#include "ace/Thread_Control.h" +#include "ace/Thread_Manager.h" + +ACE_RCSID(ace, Thread_Control, "$Id$") + +#if !defined (ACE_HAS_INLINED_OSCALLS) +# include "ace/Thread_Control.inl" +#endif /* ACE_HAS_INLINED_OS_CALLS */ + +void +ACE_Thread_Control::dump (void) const +{ + ACE_OS_TRACE ("ACE_Thread_Control::dump"); +} + +int +ACE_Thread_Control::insert (ACE_Thread_Manager *tm, int insert) +{ + ACE_OS_TRACE ("ACE_Thread_Control::insert"); + + ACE_hthread_t t_id; + ACE_OS::thr_self (t_id); + this->tm_ = tm; + + if (insert) + return this->tm_->insert_thr (ACE_OS::thr_self (), t_id); + else + return 0; +} + +// Initialize the thread controller. + +ACE_Thread_Control::ACE_Thread_Control (ACE_Thread_Manager *t, + int insert) + : tm_ (t), + status_ (0) +{ + ACE_OS_TRACE ("ACE_Thread_Control::ACE_Thread_Control"); + + if (this->tm_ != 0 && insert) + { + ACE_hthread_t t_id; + ACE_OS::thr_self (t_id); + this->tm_->insert_thr (ACE_OS::thr_self (), t_id); + } +} + +// Automatically kill thread on exit. + +ACE_Thread_Control::~ACE_Thread_Control (void) +{ + ACE_OS_TRACE ("ACE_Thread_Control::~ACE_Thread_Control"); + +#if defined (ACE_HAS_RECURSIVE_THR_EXIT_SEMANTICS) || defined (ACE_HAS_TSS_EMULATION) || defined (ACE_WIN32) + this->exit (this->status_, 0); +#else + this->exit (this->status_, 1); +#endif /* ACE_HAS_RECURSIVE_THR_EXIT_SEMANTICS */ +} + +// Exit from thread (but clean up first). + +void * +ACE_Thread_Control::exit (void *exit_status, int do_thr_exit) +{ + ACE_OS_TRACE ("ACE_Thread_Control::exit"); + + if (this->tm_ != 0) + return this->tm_->exit (exit_status, do_thr_exit); + else + { +#if !defined (ACE_HAS_TSS_EMULATION) + // With ACE_HAS_TSS_EMULATION, we let ACE_Thread_Adapter::invoke () + // exit the thread after cleaning up TSS. + ACE_OS::thr_exit (exit_status); +#endif /* ! ACE_HAS_TSS_EMULATION */ + return 0; + } +} diff --git a/ace/Threads/Thread_Control.h b/ace/Threads/Thread_Control.h new file mode 100644 index 00000000000..fbf8f0f213f --- /dev/null +++ b/ace/Threads/Thread_Control.h @@ -0,0 +1,101 @@ + +//============================================================================= +/** + * @file Thread_Control.h + * + * $Id$ + * + * @author Carlos O'Ryan + */ +//============================================================================= + + +#ifndef ACE_THREAD_CONTROL_H +#define ACE_THREAD_CONTROL_H +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if !defined (ACE_LEGACY_MODE) +# include "ace/OS.h" +#endif /* ACE_LEGACY_MODE */ + +class ACE_Thread_Manager; + +/** + * @class ACE_Thread_Control + * + * @brief Used to keep track of a thread's activities within its entry + * point function. + * + * A uses this class to ensure that threads + * it spawns automatically register and unregister themselves + * with it. + * This class can be stored in thread-specific storage using the + * wrapper. When a thread exits the + * function deletes this object, thereby + * ensuring that it gets removed from its associated + * . + */ +class ACE_Export ACE_Thread_Control +{ +public: + /// Initialize the thread control object. If != 0, then + /// register the thread with the Thread_Manager. + ACE_Thread_Control (ACE_Thread_Manager *tm = 0, + int insert = 0); + + /// Remove the thread from its associated and exit + /// the thread if is enabled. + ~ACE_Thread_Control (void); + + /// Remove this thread from its associated and exit + /// the thread if is enabled. + void *exit (void *status, + int do_thr_exit); + + /// Store the and use it to register ourselves for + /// correct shutdown. + int insert (ACE_Thread_Manager *tm, int insert = 0); + + /// Returns the current . + ACE_Thread_Manager *thr_mgr (void); + + /// Atomically set a new and return the old + /// . + ACE_Thread_Manager *thr_mgr (ACE_Thread_Manager *); + + /// Set the exit status (and return existing status). + void *status (void *status); + + /// Get the current exit status. + void *status (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Pointer to the thread manager for this block of code. + ACE_Thread_Manager *tm_; + + /// Keeps track of the exit status for the thread. + void *status_; +}; + +# if defined (ACE_HAS_INLINED_OSCALLS) +# if defined (ACE_INLINE) +# undef ACE_INLINE +# endif /* ACE_INLINE */ +# define ACE_INLINE inline +# include "ace/Thread_Control.inl" +# endif /* ACE_HAS_INLINED_OSCALLS */ + +#include "ace/post.h" +#endif /* ACE_THREAD_CONTROL_H */ diff --git a/ace/Threads/Thread_Control.inl b/ace/Threads/Thread_Control.inl new file mode 100644 index 00000000000..29cb24b1df3 --- /dev/null +++ b/ace/Threads/Thread_Control.inl @@ -0,0 +1,42 @@ +// -*- C++ -*- +// $Id$ + +// Set the exit status. + +ACE_INLINE void * +ACE_Thread_Control::status (void *s) +{ + ACE_OS_TRACE ("ACE_Thread_Control::status"); + return this->status_ = s; +} + +// Get the exit status. + +ACE_INLINE void * +ACE_Thread_Control::status (void) +{ + ACE_OS_TRACE ("ACE_Thread_Control::status"); + return this->status_; +} + +// Returns the current . + +ACE_INLINE ACE_Thread_Manager * +ACE_Thread_Control::thr_mgr (void) +{ + ACE_OS_TRACE ("ACE_Thread_Control::thr_mgr"); + return this->tm_; +} + +// Atomically set a new and return the old +// . + +ACE_INLINE ACE_Thread_Manager * +ACE_Thread_Control::thr_mgr (ACE_Thread_Manager *tm) +{ + ACE_OS_TRACE ("ACE_Thread_Control::thr_mgr"); + ACE_Thread_Manager *o_tm = this->tm_; + this->tm_ = tm; + return o_tm; +} + diff --git a/ace/Threads/Thread_Exit.cpp b/ace/Threads/Thread_Exit.cpp new file mode 100644 index 00000000000..2128ac148f5 --- /dev/null +++ b/ace/Threads/Thread_Exit.cpp @@ -0,0 +1,144 @@ +// $Id$ + +#include "ace/Thread_Exit.h" +#include "ace/OS.h" +#include "ace/Synch.h" +#include "ace/Managed_Object.h" + +ACE_RCSID(ace, Thread_Exit, "$Id$") + +u_int ACE_Thread_Exit::is_constructed_ = 0; + +#if defined (ACE_HAS_SIG_C_FUNC) +extern "C" void +ACE_Thread_Exit_cleanup (void *instance, void *arg) +{ + ACE_Thread_Exit::cleanup (instance, arg); +} +#endif + +void +ACE_Thread_Exit::cleanup (void *instance, void *) +{ + ACE_OS_TRACE ("ACE_Thread_Exit::cleanup"); + + delete (ACE_TSS_TYPE (ACE_Thread_Exit) *) instance; + + ACE_Thread_Exit::is_constructed_ = 0; + // All TSS objects have been destroyed. Reset this flag so + // ACE_Thread_Exit singleton can be created again. +} + +// NOTE: this preprocessor directive should match the one in +// ACE_Task_Base::svc_run () below. This prevents the two statics +// from being defined. + +ACE_Thread_Exit * +ACE_Thread_Exit::instance (void) +{ +#if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION) + ACE_OS_TRACE ("ACE_Thread_Exit::instance"); + + // Determines if we were dynamically allocated. + static ACE_TSS_TYPE (ACE_Thread_Exit) *instance_; + + // Implement the Double Check pattern. + + if (ACE_Thread_Exit::is_constructed_ == 0) + { + ACE_MT (ACE_Thread_Mutex *lock = + ACE_Managed_Object::get_preallocated_object + (ACE_Object_Manager::ACE_THREAD_EXIT_LOCK); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, *lock, 0)); + + if (ACE_Thread_Exit::is_constructed_ == 0) + { + ACE_NEW_RETURN (instance_, + ACE_TSS_TYPE (ACE_Thread_Exit), + 0); + + ACE_Thread_Exit::is_constructed_ = 1; + + // Register for destruction with ACE_Object_Manager. +#if defined ACE_HAS_SIG_C_FUNC + ACE_Object_Manager::at_exit (instance_, + ACE_Thread_Exit_cleanup, + 0); +#else + ACE_Object_Manager::at_exit (instance_, + ACE_Thread_Exit::cleanup, + 0); +#endif /* ACE_HAS_SIG_C_FUNC */ + } + } + + return ACE_TSS_GET (instance_, ACE_Thread_Exit); +#else + return 0; +#endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE || ACE_HAS_TSS_EMULATION */ +} + +// Grab hold of the Task * so that we can close() it in the +// destructor. + +ACE_Thread_Exit::ACE_Thread_Exit (void) +{ + ACE_OS_TRACE ("ACE_Thread_Exit::ACE_Thread_Exit"); +} + +// Set the this pointer... + +void +ACE_Thread_Exit::thr_mgr (ACE_Thread_Manager *tm) +{ + ACE_OS_TRACE ("ACE_Thread_Exit::thr_mgr"); + + if (tm != 0) + this->thread_control_.insert (tm, 0); +} + +// When this object is destroyed the Task is automatically closed +// down! + +ACE_Thread_Exit::~ACE_Thread_Exit (void) +{ + ACE_OS_TRACE ("ACE_Thread_Exit::~ACE_Thread_Exit"); +} + +ACE_Thread_Exit_Maybe::ACE_Thread_Exit_Maybe (int flag) + : instance_ (0) +{ + if (flag) + { + ACE_NEW (instance_, ACE_Thread_Exit); + } +} + +ACE_Thread_Exit_Maybe::~ACE_Thread_Exit_Maybe (void) +{ + delete this->instance_; +} + +ACE_Thread_Exit * +ACE_Thread_Exit_Maybe::operator -> (void) const +{ + return this->instance_; +} + +ACE_Thread_Exit * +ACE_Thread_Exit_Maybe::instance (void) const +{ + return this->instance_; +} + +#if (defined (ACE_HAS_THREADS) && \ + (defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || \ + defined (ACE_HAS_TSS_EMULATION))) + +# if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) + template class ACE_TSS; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_TSS +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + +#endif /* ACE_HAS_THREADS && (ACE_HAS_THREAD_SPECIFIC_STORAGE || ACE_HAS_TSS_EMULATION) */ diff --git a/ace/Threads/Thread_Exit.h b/ace/Threads/Thread_Exit.h new file mode 100644 index 00000000000..5b87817ffaa --- /dev/null +++ b/ace/Threads/Thread_Exit.h @@ -0,0 +1,110 @@ + +//============================================================================= +/** + * @file Thread_Exit.h + * + * $Id$ + * + * @author Carlos O'Ryan + */ +//============================================================================= + + +#ifndef ACE_THREAD_EXIT_H +#define ACE_THREAD_EXIT_H +#include "ace/pre.h" + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/OS.h" +#include "ace/Thread_Control.h" + +/** + * @class ACE_Thread_Exit + * + * @brief Keep exit information for a Thread in thread specific storage. + * so that the thread-specific exit hooks will get called no + * matter how the thread exits (e.g., via , C++ + * or Win32 exception, "falling off the end" of the thread entry + * point function, etc.). + * + * This clever little helper class is stored in thread-specific + * storage using the wrapper. When a thread exits the + * function deletes this object, thereby + * closing it down gracefully. + */ +class ACE_Export ACE_Thread_Exit +{ +public: + /// Capture the Thread that will be cleaned up automatically. + ACE_Thread_Exit (void); + + /// Set the . + void thr_mgr (ACE_Thread_Manager *tm); + + /// Destructor calls the thread-specific exit hooks when a thread + /// exits. + ~ACE_Thread_Exit (void); + + /// Singleton access point. + static ACE_Thread_Exit *instance (void); + + /// Cleanup method, used by the to destroy the + /// singleton. + static void cleanup (void *instance, void *); + +private: + /// Allow OS_Object_Manager to reset the status of . + friend class ACE_OS_Object_Manager; + + /// Automatically add/remove the thread from the + /// . + ACE_Thread_Control thread_control_; + + /** + * Used to detect whether we should create a new instance (or not) + * within the instance method -- we don't trust the instance_ ptr + * because the destructor may have run (if ACE::fini() was called). + * See bug #526. + * We don't follow the singleton pattern due to dependency issues. + */ + static u_int is_constructed_; +}; + +/** + * @class ACE_Thread_Exit_Maybe + * + * @brief A version of ACE_Thread_Exit that is created dynamically + * under the hood if the flag is set to TRUE. + * + * Allows the appearance of a "smart pointer", but is not + * always created. + */ +class ACE_Export ACE_Thread_Exit_Maybe +{ +public: + /// Don't create an ACE_Thread_Exit instance by default. + ACE_Thread_Exit_Maybe (int flag = 0); + + /// Destroys the underlying ACE_Thread_Exit instance if it exists. + ~ACE_Thread_Exit_Maybe (void); + + /// Delegates to underlying instance. + ACE_Thread_Exit * operator -> (void) const; + + /// Returns the underlying instance. + ACE_Thread_Exit * instance (void) const; + +private: + + /// Holds the underlying instance. + ACE_Thread_Exit *instance_; + +}; + +#include "ace/post.h" +#endif /* ACE_THREAD_EXIT_H */ diff --git a/ace/Threads/Thread_Manager.cpp b/ace/Threads/Thread_Manager.cpp new file mode 100644 index 00000000000..fdf9c1b532a --- /dev/null +++ b/ace/Threads/Thread_Manager.cpp @@ -0,0 +1,2238 @@ +// $Id$ + +#include "ace/Synch_T.h" +#include "ace/Thread_Manager.h" +#include "ace/Dynamic.h" +#include "ace/Object_Manager.h" +#include "ace/Singleton.h" +#include "ace/Auto_Ptr.h" +#include "ace/Thread_Exit.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Thread_Manager.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Thread_Manager, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Control) +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Manager) + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) +// Process-wide Thread Manager. +ACE_Thread_Manager *ACE_Thread_Manager::thr_mgr_ = 0; + +// Controls whether the Thread_Manager is deleted when we shut down +// (we can only delete it safely if we created it!) +int ACE_Thread_Manager::delete_thr_mgr_ = 0; +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + +void +ACE_Thread_Manager::dump (void) +{ + ACE_TRACE ("ACE_Thread_Manager::dump"); + // Cast away const-ness of this in order to use its non-const lock_. + ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_mon, + ((ACE_Thread_Manager *) this)->lock_)); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ngrp_id_ = %d"), this->grp_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncurrent_count_ = %d"), this->thr_list_.size ())); + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + iter.next ()->dump (); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Thread_Descriptor::~ACE_Thread_Descriptor (void) +{ + delete this->sync_; +} + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) +void ACE_Thread_Descriptor::at_pop (int apply) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_pop"); + // Get first at from at_exit_list + ACE_At_Thread_Exit* at = this->at_exit_list_; + // Remove at from at_exit list + this->at_exit_list_ = at->next_; + // Apply if required + if (apply) + { + at->apply (); + // Do the apply method + at->was_applied (1); + // Mark at has been applied to avoid double apply from + // at destructor + } + // If at is not owner delete at. + if (!at->is_owner ()) + delete at; +} + +void +ACE_Thread_Descriptor::at_push (ACE_At_Thread_Exit* cleanup, int is_owner) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_push"); + cleanup->is_owner (is_owner); + cleanup->td_ = this; + cleanup->next_ = at_exit_list_; + at_exit_list_ = cleanup; +} + +int +ACE_Thread_Descriptor::at_exit (ACE_At_Thread_Exit& cleanup) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_exit"); + at_push (&cleanup, 1); + return 0; +} + +int +ACE_Thread_Descriptor::at_exit (ACE_At_Thread_Exit* cleanup) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_exit"); + if (cleanup==0) + return -1; + else + { + this->at_push (cleanup); + return 0; + } +} + +void +ACE_Thread_Descriptor::do_at_exit () + +{ + ACE_TRACE ("ACE_Thread_Descriptor::do_at_exit"); + while (at_exit_list_!=0) + this->at_pop (); +} + +void +ACE_Thread_Descriptor::terminate () + +{ + ACE_TRACE ("ACE_Thread_Descriptor::terminate"); + + if (!terminated_) + { + ACE_Log_Msg* log_msg = this->log_msg_; + terminated_ = 1; + // Run at_exit hooks + this->do_at_exit (); + // We must remove Thread_Descriptor from Thread_Manager list + if (this->tm_ != 0) + { + int close_handle = 0; + +#if !defined (VXWORKS) + // Threads created with THR_DAEMON shouldn't exist here, but + // just to be safe, let's put it here. + + if (ACE_BIT_DISABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_JOINING)) + { + if (ACE_BIT_DISABLED (this->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (this->flags_, THR_JOINABLE)) + { + // Mark thread as terminated. + ACE_SET_BITS (this->thr_state_, ACE_Thread_Manager::ACE_THR_TERMINATED); + tm_->register_as_terminated (this); + // Must copy the information here because td will be + // "freed" below. + } +#if defined (ACE_WIN32) + else + { + close_handle = 1; + } +#endif /* ACE_WIN32 */ + } +#endif /* ! VXWORKS */ + + // Remove thread descriptor from the table. + if (this->tm_ != 0) + tm_->remove_thr (this, close_handle); + } + + // Check if we need delete ACE_Log_Msg instance + // If ACE_TSS_cleanup was not executed first log_msg == 0 + if (log_msg == 0) + { + // Only inform to ACE_TSS_cleanup that it must delete the log instance + // setting ACE_LOG_MSG thr_desc to 0. + ACE_LOG_MSG->thr_desc (0); + } + else + { + // Thread_Descriptor is the owner of the Log_Msg instance!! + // deleted. + this->log_msg_ = 0; + delete log_msg; + } + } +} + +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +int +ACE_Thread_Descriptor::at_exit (void *object, + ACE_CLEANUP_FUNC cleanup_hook, + void *param) +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_exit"); +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + this->cleanup_info_.cleanup_hook_ = cleanup_hook; + this->cleanup_info_.object_ = object; + this->cleanup_info_.param_ = param; +#else + // To keep compatibility, when cleanup_hook is null really is a at_pop + // without apply. + if (cleanup_hook == 0) + { + if (this->at_exit_list_!= 0) + this->at_pop(0); + } + else + { + ACE_At_Thread_Exit* cleanup; + ACE_NEW_RETURN (cleanup, + ACE_At_Thread_Exit_Func (object, + cleanup_hook, + param), + -1); + this->at_push (cleanup); + } +#endif /* ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + return 0; +} + +void +ACE_Thread_Descriptor::dump (void) const +{ + ACE_TRACE ("ACE_Thread_Descriptor::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_id_ = %d"), this->thr_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_handle_ = %d"), this->thr_handle_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ngrp_id_ = %d"), this->grp_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_state_ = %d"), this->thr_state_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncleanup_info_.cleanup_hook_ = %x"), this->cleanup_info_.cleanup_hook_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nflags_ = %x\n"), this->flags_)); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Thread_Descriptor::ACE_Thread_Descriptor (void) + : +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + log_msg_ (0), + at_exit_list_ (0), + terminated_ (0) +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ +{ + ACE_TRACE ("ACE_Thread_Descriptor::ACE_Thread_Descriptor"); + ACE_NEW (this->sync_, + ACE_DEFAULT_THREAD_MANAGER_LOCK); +} + +void +ACE_Thread_Descriptor::acquire_release (void) +{ + // Just try to acquire the lock then release it. +#if defined (ACE_THREAD_MANAGER_USES_SAFE_SPAWN) + if (ACE_BIT_DISABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_SPAWNED)) +#endif /* ACE_THREAD_MANAGER_USES_SAFE_SPAWN */ + { + this->sync_->acquire (); + // Acquire the lock before removing from the thread table. If + // this thread is in the table already, it should simply acquire the + // lock easily. + + // Once we get the lock, we must have registered. + ACE_ASSERT (ACE_BIT_ENABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_SPAWNED)); + + this->sync_->release (); + // Release the lock before putting it back to freelist. + } +} + +// The following macro simplifies subsequence code. +#define ACE_FIND(OP,INDEX) \ + ACE_Thread_Descriptor *INDEX = OP; \ + +ACE_Thread_Descriptor * +ACE_Thread_Manager::thread_descriptor (ACE_thread_t thr_id) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_descriptor"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + ACE_FIND (this->find_thread (thr_id), ptr); + return ptr; +} + +ACE_Thread_Descriptor * +ACE_Thread_Manager::hthread_descriptor (ACE_hthread_t thr_handle) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_descriptor"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + ACE_FIND (this->find_hthread (thr_handle), ptr); + return ptr; +} + +// Return the thread descriptor (indexed by ACE_hthread_t). + +int +ACE_Thread_Manager::thr_self (ACE_hthread_t &self) +{ + ACE_TRACE ("ACE_Thread_Manager::thr_self"); + + ACE_Thread_Descriptor *desc = + this->thread_desc_self (); + + if (desc == 0) + return -1; + else + desc->self (self); + + return 0; +} + +// Initialize the synchronization variables. + +ACE_Thread_Manager::ACE_Thread_Manager (size_t prealloc, + size_t lwm, + size_t inc, + size_t hwm) + : grp_id_ (1), + automatic_wait_ (1) +#if defined (ACE_HAS_THREADS) + , zero_cond_ (lock_) +#endif /* ACE_HAS_THREADS */ + , thread_desc_freelist_ (ACE_FREE_LIST_WITH_POOL, + prealloc, lwm, hwm, inc) +{ + ACE_TRACE ("ACE_Thread_Manager::ACE_Thread_Manager"); +} + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) +ACE_Thread_Manager * +ACE_Thread_Manager::instance (void) +{ + ACE_TRACE ("ACE_Thread_Manager::instance"); + + if (ACE_Thread_Manager::thr_mgr_ == 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_Thread_Manager::thr_mgr_ == 0) + { + ACE_NEW_RETURN (ACE_Thread_Manager::thr_mgr_, + ACE_Thread_Manager, + 0); + ACE_Thread_Manager::delete_thr_mgr_ = 1; + } + } + + return ACE_Thread_Manager::thr_mgr_; +} + +ACE_Thread_Manager * +ACE_Thread_Manager::instance (ACE_Thread_Manager *tm) +{ + ACE_TRACE ("ACE_Thread_Manager::instance"); + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + ACE_Thread_Manager *t = ACE_Thread_Manager::thr_mgr_; + // We can't safely delete it since we don't know who created it! + ACE_Thread_Manager::delete_thr_mgr_ = 0; + + ACE_Thread_Manager::thr_mgr_ = tm; + return t; +} + +void +ACE_Thread_Manager::close_singleton (void) +{ + ACE_TRACE ("ACE_Thread_Manager::close_singleton"); + + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance ())); + + if (ACE_Thread_Manager::delete_thr_mgr_) + { + // First, we clean up the thread descriptor list. + ACE_Thread_Manager::thr_mgr_->close (); + delete ACE_Thread_Manager::thr_mgr_; + ACE_Thread_Manager::thr_mgr_ = 0; + ACE_Thread_Manager::delete_thr_mgr_ = 0; + } +} +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + +// Close up and release all resources. + +int +ACE_Thread_Manager::close () +{ + ACE_TRACE ("ACE_Thread_Manager::close"); + + // Clean up the thread descriptor list. + if (this->automatic_wait_) + this->wait (0, 1); + else + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + this->remove_thr_all (); + } + + return 0; +} + +ACE_Thread_Manager::~ACE_Thread_Manager (void) +{ + ACE_TRACE ("ACE_Thread_Manager::~ACE_Thread_Manager"); + this->close (); +} + + +// Run the entry point for thread spawned under the control of the +// . This must be an extern "C" to make certain +// compilers happy... +// +// The interaction with and +// works like this, with +// ACE_HAS_THREAD_SPECIFIC_STORAGE or ACE_HAS_TSS_EMULATION: +// +// o Every thread in the is run with +// . +// +// o retrieves the singleton +// instance from . +// The singleton gets created in thread-specific storage +// in the first call to that function. The key point is that the +// instance is in thread-specific storage. +// +// o A thread can exit by various means, such as , C++ +// or Win32 exception, "falling off the end" of the thread entry +// point function, etc. +// +// o If you follow this so far, now it gets really fun . . . +// When the thread-specific storage (for the thread that +// is being destroyed) is cleaned up, the OS threads package (or +// the ACE emulation of thread-specific storage) will destroy any +// objects that are in thread-specific storage. It has a list of +// them, and just walks down the list and destroys each one. +// +// o That's where the ACE_Thread_Exit destructor gets called. + +#if defined(ACE_USE_THREAD_MANAGER_ADAPTER) +extern "C" void * +ace_thread_manager_adapter (void *args) +{ +#if defined (ACE_HAS_TSS_EMULATION) + // As early as we can in the execution of the new thread, allocate + // its local TS storage. Allocate it on the stack, to save dynamic + // allocation/dealloction. + void *ts_storage[ACE_TSS_Emulation::ACE_TSS_THREAD_KEYS_MAX]; + ACE_TSS_Emulation::tss_open (ts_storage); +#endif /* ACE_HAS_TSS_EMULATION */ + + ACE_Thread_Adapter *thread_args = (ACE_Thread_Adapter *) args; + + // NOTE: this preprocessor directive should match the one in above + // ACE_Thread_Exit::instance (). With the Xavier Pthreads package, + // the exit_hook in TSS causes a seg fault. So, this works around + // that by creating exit_hook on the stack. +#if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION) + // Obtain our thread-specific exit hook and make sure that it knows + // how to clean us up! Note that we never use this pointer directly + // (it's stored in thread-specific storage), so it's ok to + // dereference it here and only store it as a reference. + ACE_Thread_Exit &exit_hook = *ACE_Thread_Exit::instance (); +#else + // Without TSS, create an instance. When this + // function returns, its destructor will be called because the + // object goes out of scope. The drawback with this appraoch is + // that the destructor _won't_ get called if is called. + // So, threads shouldn't exit that way. Instead, they should return + // from . + ACE_Thread_Exit exit_hook; +#endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE || ACE_HAS_TSS_EMULATION */ + + // Keep track of the that's associated with this + // . + exit_hook.thr_mgr (thread_args->thr_mgr ()); + + // Invoke the user-supplied function with the args. + void *status = thread_args->invoke (); + + return status; +} +#endif + +// Call the appropriate OS routine to spawn a thread. Should *not* be +// called with the lock_ held... + +int +ACE_Thread_Manager::spawn_i (ACE_THR_FUNC func, + void *args, + long flags, + ACE_thread_t *t_id, + ACE_hthread_t *t_handle, + long priority, + int grp_id, + void *stack, + size_t stack_size, + ACE_Task_Base *task) +{ + // First, threads created by Thread Manager should not be daemon threads. + // Using assertion is probably a bit too strong. However, it helps + // finding this kind of error as early as possible. Perhaps we can replace + // assertion by returning error. + ACE_ASSERT (ACE_BIT_DISABLED (flags, THR_DAEMON)); + + // Create a new thread running . *Must* be called with the + // held... + // Get a "new" Thread Descriptor from the freelist. + auto_ptr new_thr_desc (this->thread_desc_freelist_.remove ()); + + // Reset thread descriptor status + new_thr_desc->reset (this); + + ACE_Thread_Adapter *thread_args = 0; +# if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS) + ACE_NEW_RETURN (thread_args, + ACE_Thread_Adapter (func, + args, + (ACE_THR_C_FUNC) ace_thread_adapter, + this, + new_thr_desc.get (), + ACE_OS_Object_Manager::seh_except_selector(), + ACE_OS_Object_Manager::seh_except_handler()), + -1); +#else + ACE_NEW_RETURN (thread_args, + ACE_Thread_Adapter (func, + args, + (ACE_THR_C_FUNC) ace_thread_adapter, + this, + new_thr_desc.get ()), + -1); +# endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */ + + ACE_TRACE ("ACE_Thread_Manager::spawn_i"); + ACE_hthread_t thr_handle; + +#if defined (VXWORKS) && ! defined (ACE_HAS_PACE) + // On VxWorks, ACE_thread_t is char *. If t_id is 0, allocate space + // for ACE_OS::thr_create () to store the task name. If t_id is not + // 0, and it doesn't point to a 0 char *, then the non-zero char * + // will be used for the task name in ACE_OS::thr_create (). If t_id + // is not 0, but does point to a 0 char *, the t_id will be set to + // point to the task name in the TCB in ACE_OS::thr_create (). + if (t_id == 0) + { + char *thr_id; + ACE_NEW_RETURN (thr_id, + char[16], + -1); + // Mark the thread ID to show that the ACE_Thread_Manager + // allocated it. + thr_id[0] = ACE_THR_ID_ALLOCATED; + thr_id[1] = '\0'; + t_id = &thr_id; + } +#else /* ! VXWORKS */ + ACE_thread_t thr_id; + if (t_id == 0) + t_id = &thr_id; +#endif /* ! VXWORKS */ + + new_thr_desc->sync_->acquire (); + // Acquire the lock to block the spawned thread from + // removing this Thread Descriptor before it gets put into our + // thread table. + + int result = ACE_Thread::spawn (func, + args, + flags, + t_id, + &thr_handle, + priority, + stack, + stack_size, + thread_args); + + if (result != 0) + { + // _Don't_ clobber errno here! result is either 0 or -1, and + // ACE_OS::thr_create () already set errno! D. Levine 28 Mar 1997 + // errno = result; + ACE_Errno_Guard guard (errno); // Lock release may smash errno + new_thr_desc->sync_->release (); + return -1; + } + else + { +#if defined (ACE_HAS_WTHREADS) + // Have to duplicate handle if client asks for it. + // @@ How are thread handles implemented on AIX? Do they + // also need to be duplicated? + if (t_handle != 0) +# if defined (ACE_HAS_WINCE) + *t_handle = thr_handle; +# else /* ! ACE_HAS_WINCE */ + (void) ::DuplicateHandle (::GetCurrentProcess (), + thr_handle, + ::GetCurrentProcess (), + t_handle, + 0, + TRUE, + DUPLICATE_SAME_ACCESS); +# endif /* ! ACE_HAS_WINCE */ +#elif defined (VXWORKS) + if (t_handle != 0) + *t_handle = thr_handle; +#else /* ! ACE_HAS_WTHREADS && ! VXWORKS */ + ACE_UNUSED_ARG (t_handle); +#endif /* ! ACE_HAS_WTHREADS && ! VXWORKS */ + + // append_thr also put the into Thread_Manager's + // double-linked list. Only after this point, can we manipulate + // double-linked list from a spawned thread's context. + return this->append_thr (*t_id, + thr_handle, + ACE_THR_SPAWNED, + grp_id, + task, + flags, + new_thr_desc.release ()); + } +} + +int +ACE_Thread_Manager::spawn (ACE_THR_FUNC func, + void *args, + long flags, + ACE_thread_t *t_id, + ACE_hthread_t *t_handle, + long priority, + int grp_id, + void *stack, + size_t stack_size) +{ + ACE_TRACE ("ACE_Thread_Manager::spawn"); + + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (grp_id == -1) + grp_id = this->grp_id_++; // Increment the group id. + + if (this->spawn_i (func, args, flags, t_id, t_handle, + priority, grp_id, stack, stack_size) == -1) + return -1; + + return grp_id; +} + +// Create N new threads running FUNC. + +int +ACE_Thread_Manager::spawn_n (size_t n, + ACE_THR_FUNC func, + void *args, + long flags, + long priority, + int grp_id, + ACE_Task_Base *task, + ACE_hthread_t thread_handles[], + void *stack[], + size_t stack_size[]) +{ + ACE_TRACE ("ACE_Thread_Manager::spawn_n"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (grp_id == -1) + grp_id = this->grp_id_++; // Increment the group id. + + for (size_t i = 0; i < n; i++) + { + // @@ What should happen if this fails?! e.g., should we try to + // cancel the other threads that we've already spawned or what? + if (this->spawn_i (func, + args, + flags, + 0, + thread_handles == 0 ? 0 : &thread_handles[i], + priority, + grp_id, + stack == 0 ? 0 : stack[i], + stack_size == 0 ? 0 : stack_size[i], + task) == -1) + return -1; + } + + return grp_id; +} + +// Create N new threads running FUNC. + +int +ACE_Thread_Manager::spawn_n (ACE_thread_t thread_ids[], + size_t n, + ACE_THR_FUNC func, + void *args, + long flags, + long priority, + int grp_id, + void *stack[], + size_t stack_size[], + ACE_hthread_t thread_handles[], + ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::spawn_n"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (grp_id == -1) + grp_id = this->grp_id_++; // Increment the group id. + + for (size_t i = 0; i < n; i++) + { + // @@ What should happen if this fails?! e.g., should we try to + // cancel the other threads that we've already spawned or what? + if (this->spawn_i (func, + args, + flags, + thread_ids == 0 ? 0 : &thread_ids[i], + thread_handles == 0 ? 0 : &thread_handles[i], + priority, + grp_id, + stack == 0 ? 0 : stack[i], + stack_size == 0 ? 0 : stack_size[i], + task) == -1) + return -1; + } + + return grp_id; +} + +// Append a thread into the pool (does not check for duplicates). +// Must be called with locks held. + +int +ACE_Thread_Manager::append_thr (ACE_thread_t t_id, + ACE_hthread_t t_handle, + ACE_UINT32 thr_state, + int grp_id, + ACE_Task_Base *task, + long flags, + ACE_Thread_Descriptor *td) +{ + ACE_TRACE ("ACE_Thread_Manager::append_thr"); + ACE_Thread_Descriptor *thr_desc; + + if (td == 0) + { + ACE_NEW_RETURN (thr_desc, + ACE_Thread_Descriptor, + -1); +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + thr_desc->tm_ = this; + // Setup the Thread_Manager. +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + } + else + thr_desc = td; + + thr_desc->thr_id_ = t_id; + thr_desc->thr_handle_ = t_handle; + thr_desc->grp_id_ = grp_id; + thr_desc->task_ = task; + thr_desc->flags_ = flags; + + this->thr_list_.insert_head (thr_desc); + ACE_SET_BITS (thr_desc->thr_state_, thr_state); + thr_desc->sync_->release (); + + return 0; +} + +// Return the thread descriptor (indexed by ACE_hthread_t). + +ACE_Thread_Descriptor * +ACE_Thread_Manager::find_hthread (ACE_hthread_t h_id) +{ + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_cmp (iter.next ()->thr_handle_, h_id)) + return iter.next (); + + return 0; +} + +// Locate the index in the table associated with . Must be +// called with the lock held. + +ACE_Thread_Descriptor * +ACE_Thread_Manager::find_thread (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::find_thread"); + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_equal (iter.next ()->thr_id_, t_id)) + return iter.next (); + return 0; +} + +// Insert a thread into the pool (checks for duplicates and doesn't +// allow them to be inserted twice). + +int +ACE_Thread_Manager::insert_thr (ACE_thread_t t_id, + ACE_hthread_t t_handle, + int grp_id, + long flags) +{ + ACE_TRACE ("ACE_Thread_Manager::insert_thr"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + // Check for duplicates and bail out if we're already registered... +#if defined (VXWORKS) + if (this->find_hthread (t_handle) != 0 ) + return -1; +#else /* ! VXWORKS */ + if (this->find_thread (t_id) != 0 ) + return -1; +#endif /* ! VXWORKS */ + + if (grp_id == -1) + grp_id = this->grp_id_++; + + if (this->append_thr (t_id, + t_handle, + ACE_THR_SPAWNED, + grp_id, + 0, + flags) == -1) + return -1; + + return grp_id; +} + +// Run the registered hooks when the thread exits. + +void +ACE_Thread_Manager::run_thread_exit_hooks (int i) +{ +#if 0 // currently unused! + ACE_TRACE ("ACE_Thread_Manager::run_thread_exit_hooks"); + + // @@ Currently, we have just one hook. This should clearly be + // generalized to support an arbitrary number of hooks. + + ACE_Thread_Descriptor *td = this->thread_desc_self (); + if (td != 0 && td->cleanup_info.cleanup_hook_ != 0) + { + (*td->cleanup_info_.cleanup_hook_) + (td->cleanup_info_.object_, + td->cleanup_info_.param_); + + td->cleanup_info_.cleanup_hook_ = 0; + } + ACE_UNUSED_ARG (i); +#else + ACE_UNUSED_ARG (i); +#endif /* 0 */ +} + +// Remove a thread from the pool. Must be called with locks held. + +void +ACE_Thread_Manager::remove_thr (ACE_Thread_Descriptor *td, + int close_handler) +{ + ACE_TRACE ("ACE_Thread_Manager::remove_thr"); + +#if defined (VXWORKS) + ACE_thread_t tid = td->self (); +#endif /* VXWORKS */ + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + td->tm_ = 0; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + this->thr_list_.remove (td); + +#if defined (VXWORKS) && ! defined (ACE_HAS_PACE) + // Delete the thread ID, if the ACE_Thread_Manager allocated it. + if (tid && tid[0] == ACE_THR_ID_ALLOCATED) + { + delete [] tid; + } +#endif /* VXWORKS */ + +#if defined (ACE_WIN32) + if (close_handler != 0) + ::CloseHandle (td->thr_handle_); +#else + ACE_UNUSED_ARG (close_handler); +#endif /* ACE_WIN32 */ + +#if 1 + + this->thread_desc_freelist_.add (td); +#else + delete td; +#endif /* 1 */ + +#if defined (ACE_HAS_THREADS) + // Tell all waiters when there are no more threads left in the pool. + if (this->thr_list_.size () == 0) + this->zero_cond_.broadcast (); +#endif /* ACE_HAS_THREADS */ +} + +// Repeatedly call remove_thr on all table entries until there +// is no thread left. Must be called with lock held. + +void +ACE_Thread_Manager::remove_thr_all (void) +{ + ACE_Thread_Descriptor *td; + + while ((td = this->thr_list_.delete_head ()) != 0) + { +#if defined (ACE_WIN32) + // We need to let go handles if we want to let the threads + // run wild. + // @@ Do we need to close down AIX thread handles too? + ::CloseHandle (td->thr_handle_); +#endif /* ACE_WIN32 */ + delete td; + } + +} + +// ------------------------------------------------------------------ +// Factor out some common behavior to simplify the following methods. +#define ACE_THR_OP(OP,STATE) \ + int result = OP (td->thr_handle_); \ + if (result == -1) { \ + if (errno != ENOTSUP) \ + this->thr_to_be_removed_.enqueue_tail (td); \ + return -1; \ + } \ + else { \ + ACE_SET_BITS (td->thr_state_, STATE); \ + return 0; \ + } + +int +ACE_Thread_Manager::join_thr (ACE_Thread_Descriptor *td, int) +{ + ACE_TRACE ("ACE_Thread_Manager::join_thr"); +#if defined (ACE_HAS_PACE) + return ACE_Thread::join (td->thr_handle_); +#else + int result = ACE_Thread::join (td->thr_handle_); + if (result != 0) + { + // Since the thread are being joined, we should + // let it remove itself from the list. + + // this->remove_thr (td); + errno = result; + return -1; + } + + return 0; +#endif /* ACE_HAS_PACE */ +} + +int +ACE_Thread_Manager::suspend_thr (ACE_Thread_Descriptor *td, int) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_thr"); + + int result = ACE_Thread::suspend (td->thr_handle_); + if (result == -1) { + if (errno != ENOTSUP) + this->thr_to_be_removed_.enqueue_tail (td); + return -1; + } + else { + ACE_SET_BITS (td->thr_state_, ACE_THR_SUSPENDED); + return 0; + } +} + +int +ACE_Thread_Manager::resume_thr (ACE_Thread_Descriptor *td, int) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_thr"); + + int result = ACE_Thread::resume (td->thr_handle_); + if (result == -1) { + if (errno != ENOTSUP) + this->thr_to_be_removed_.enqueue_tail (td); + return -1; + } + else { + ACE_CLR_BITS (td->thr_state_, ACE_THR_SUSPENDED); + return 0; + } +} + +int +ACE_Thread_Manager::cancel_thr (ACE_Thread_Descriptor *td, int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_thr"); + // Must set the state first and then try to cancel the thread. + ACE_SET_BITS (td->thr_state_, ACE_THR_CANCELLED); + + if (async_cancel != 0) + // Note that this call only does something relevant if the OS + // platform supports asynchronous thread cancellation. Otherwise, + // it's a no-op. + return ACE_Thread::cancel (td->thr_id_); + + return 0; +} + +int +ACE_Thread_Manager::kill_thr (ACE_Thread_Descriptor *td, int signum) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_thr"); + + ACE_thread_t tid = td->thr_id_; +#if defined (VXWORKS) && ! defined (ACE_HAS_PACE) + // Skip over the ID-allocated marker, if present. + tid += tid[0] == ACE_THR_ID_ALLOCATED ? 1 : 0; +#endif /* VXWORKS */ + + int result = ACE_Thread::kill (tid, signum); + + if (result != 0) + { + // Only remove a thread from us when there is a "real" error. + if (errno != ENOTSUP) + this->thr_to_be_removed_.enqueue_tail (td); + + return -1; + } +#if defined (CHORUS) + else if (signum == SIGTHREADKILL) + this->thr_to_be_removed_.enqueue_tail (td); +#endif /* CHORUS */ + + return 0; +} + +// ------------------------------------------------------------------ +// Factor out some common behavior to simplify the following methods. +#define ACE_EXECUTE_OP(OP, ARG) \ + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); \ + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); \ + ACE_FIND (this->find_thread (t_id), ptr); \ + if (ptr == 0) \ + { \ + errno = ENOENT; \ + return -1; \ + } \ + int result = OP (ptr, ARG); \ + ACE_Errno_Guard error (errno); \ + while (! this->thr_to_be_removed_.is_empty ()) { \ + ACE_Thread_Descriptor *td; \ + this->thr_to_be_removed_.dequeue_head (td); \ + this->remove_thr (td, 1); \ + } \ + return result + +// Suspend a single thread. + +int +ACE_Thread_Manager::suspend (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend"); + ACE_EXECUTE_OP (this->suspend_thr, 0); +} + +// Resume a single thread. + +int +ACE_Thread_Manager::resume (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::resume"); + ACE_EXECUTE_OP (this->resume_thr, 0); +} + +// Cancel a single thread. + +int +ACE_Thread_Manager::cancel (ACE_thread_t t_id, int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel"); + ACE_EXECUTE_OP (this->cancel_thr, async_cancel); +} + +// Send a signal to a single thread. + +int +ACE_Thread_Manager::kill (ACE_thread_t t_id, int signum) +{ + ACE_TRACE ("ACE_Thread_Manager::kill"); + ACE_EXECUTE_OP (this->kill_thr, signum); +} + +int +ACE_Thread_Manager::check_state (ACE_UINT32 state, + ACE_thread_t id, + int enable) +{ + ACE_TRACE ("ACE_Thread_Manager::check_state"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_UINT32 thr_state; + + int self_check = ACE_OS::thr_equal (id, ACE_OS::thr_self ()); + + // If we're checking the state of our thread, try to get the cached + // value out of TSS to avoid lookup. + if (self_check) + thr_state = this->thread_desc_self ()->thr_state_; + else + { + // Not calling from self, have to look it up from the list. + ACE_FIND (this->find_thread (id), ptr); + if (ptr == 0) + return 0; + thr_state = ptr->thr_state_; + } + if (enable) + return ACE_BIT_ENABLED (thr_state, state); + + return ACE_BIT_DISABLED (thr_state, state); +} + +// Test if a single thread is suspended. + +int +ACE_Thread_Manager::testsuspend (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testsuspend"); + return this->check_state (ACE_THR_SUSPENDED, t_id); +} + +// Test if a single thread is active (i.e., resumed). + +int +ACE_Thread_Manager::testresume (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testresume"); + return this->check_state (ACE_THR_SUSPENDED, t_id, 0); +} + +// Test if a single thread is cancelled. + +int +ACE_Thread_Manager::testcancel (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testcancel"); + return this->check_state (ACE_THR_CANCELLED, t_id); +} + +// Thread information query functions. + +int +ACE_Thread_Manager::hthread_within (ACE_hthread_t handle) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_within"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_monx, this->lock_, -1)); + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_cmp(iter.next ()->thr_handle_, handle)) + return 1; + + return 0; +} + +int +ACE_Thread_Manager::thread_within (ACE_thread_t tid) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_within"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_monx, this->lock_, -1)); + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_equal (iter.next ()->thr_id_, tid)) + return 1; + + return 0; +} + +// Get group ids for a particular thread id. + +int +ACE_Thread_Manager::get_grp (ACE_thread_t t_id, int &grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::get_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_FIND (this->find_thread (t_id), ptr); + + if (ptr) + grp_id = ptr->grp_id_; + else + return -1; + return 0; +} + +// Set group ids for a particular thread id. + +int +ACE_Thread_Manager::set_grp (ACE_thread_t t_id, int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::set_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_FIND (this->find_thread (t_id), ptr); + if (ptr) + ptr->grp_id_ = grp_id; + else + return -1; + return 0; +} + +// Suspend a group of threads. + +int +ACE_Thread_Manager::apply_grp (int grp_id, + ACE_THR_MEMBER_FUNC func, + int arg) +{ + ACE_TRACE ("ACE_Thread_Manager::apply_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_monx, this->lock_, -1)); + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + + int result = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->grp_id_ == grp_id) + if ((this->*func) (iter.next (), arg) == -1) + result = -1; + + // Must remove threads after we have traversed the thr_list_ to + // prevent clobber thr_list_'s integrity. + + if (! this->thr_to_be_removed_.is_empty ()) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + + for (ACE_Thread_Descriptor *td; + this->thr_to_be_removed_.dequeue_head (td) != -1; + ) + this->remove_thr (td, 1); + } + + return result; +} + +int +ACE_Thread_Manager::suspend_grp (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::suspend_thr)); +} + +// Resume a group of threads. + +int +ACE_Thread_Manager::resume_grp (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::resume_thr)); +} + +// Kill a group of threads. + +int +ACE_Thread_Manager::kill_grp (int grp_id, int signum) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::kill_thr), signum); +} + +// Cancel a group of threads. + +int +ACE_Thread_Manager::cancel_grp (int grp_id, int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::cancel_thr), + async_cancel); +} + +int +ACE_Thread_Manager::apply_all (ACE_THR_MEMBER_FUNC func, int arg) +{ + ACE_TRACE ("ACE_Thread_Manager::apply_all"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + + int result = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if ((this->*func)(iter.next (), arg) == -1) + result = -1; + + // Must remove threads after we have traversed the thr_list_ to + // prevent clobber thr_list_'s integrity. + + if (! this->thr_to_be_removed_.is_empty ()) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + + for (ACE_Thread_Descriptor *td; + this->thr_to_be_removed_.dequeue_head (td) != -1; + ) + this->remove_thr (td, 1); + } + + return result; +} + +// Resume all threads that are suspended. + +int +ACE_Thread_Manager::resume_all (void) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_all"); + return this->apply_all (ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::resume_thr)); +} + +int +ACE_Thread_Manager::suspend_all (void) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_all"); + return this->apply_all (ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::suspend_thr)); +} + +int +ACE_Thread_Manager::kill_all (int sig) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_all"); + return this->apply_all (&ACE_Thread_Manager::kill_thr, sig); +} + +int +ACE_Thread_Manager::cancel_all (int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_all"); + return this->apply_all (ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::cancel_thr), + async_cancel); +} + +int +ACE_Thread_Manager::join (ACE_thread_t tid, void **status) +{ + ACE_TRACE ("ACE_Thread_Manager::join"); + + ACE_Thread_Descriptor_Base tdb; + int found = 0; + + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + +#if !defined (VXWORKS) + for (ACE_Double_Linked_List_Iterator biter (this->terminated_thr_list_); + !biter.done (); + biter.advance ()) + if (ACE_OS::thr_equal (biter.next ()->thr_id_, tid)) + { + ACE_Thread_Descriptor_Base *tdb = biter.advance_and_remove (0); + if (ACE_Thread::join (tdb->thr_handle_) == -1) + return -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + ::pthread_detach (&tdb->thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + + delete tdb; + return 0; + // return immediately if we've found the thread we want to join. + } +#endif /* !VXWORKS */ + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't help much. + if (ACE_OS::thr_equal (iter.next ()->thr_id_,tid) && + (ACE_BIT_DISABLED (iter.next ()->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (iter.next ()->flags_, THR_JOINABLE))) + { + tdb = *iter.next (); + ACE_SET_BITS (iter.next ()->thr_state_, ACE_THR_JOINING); + found = 1; + break; + } + + if (found == 0) + return -1; + // Didn't find the thread we want or the thread is not joinable. + } + +# if defined (_AIX) + // The AIX xlC compiler does not match the proper function here - it + // confuses ACE_Thread::join(ACE_thread_t, ACE_thread_t *, void **=0) and + // ACE_Thread::join(ACE_hthread_t, void **=0). At least at 3.1.4.7 and .8. + // The 2nd arg is ignored for pthreads anyway. + + // And, g++ on AIX needs the three-arg thr_join, also, to pick up the + // proper version from the AIX libraries. + if (ACE_Thread::join (tdb.thr_handle_, &tdb.thr_handle_, status) == -1) +# else /* ! _AIX */ + if (ACE_Thread::join (tdb.thr_handle_, status) == -1) +# endif /* ! _AIX */ + return -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + +# if defined (HPUX_10) + // HP-UX DCE threads' pthread_detach will smash thr_id if it's just given + // as an argument. Since the thread handle is still needed, give + // pthread_detach a junker to scribble on. + ACE_thread_t junker; + cma_handle_assign(&tdb.thr_handle_, &junker); + ::pthread_detach (&junker); +# else + ::pthread_detach (&tdb.thr_handle_); + #endif /* HPUX_10 */ +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + return 0; +} + +// Wait for group of threads + +int +ACE_Thread_Manager::wait_grp (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::wait_grp"); + + int copy_count = 0; + ACE_Thread_Descriptor_Base *copy_table = 0; + + // We have to make sure that while we wait for these threads to + // exit, we do not have the lock. Therefore we make a copy of all + // interesting entries and let go of the lock. + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + +#if !defined (VXWORKS) + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size () + + this->terminated_thr_list_.size ()], + -1); +#else + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size ()], + -1); +#endif /* VXWORKS */ + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't help much. + if (iter.next ()->grp_id_ == grp_id && + (ACE_BIT_DISABLED (iter.next ()->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (iter.next ()->flags_, THR_JOINABLE))) + { + ACE_SET_BITS (iter.next ()->thr_state_, ACE_THR_JOINING); + copy_table[copy_count++] = *iter.next (); + } + +#if !defined (VXWORKS) + for (ACE_Double_Linked_List_Iterator biter (this->terminated_thr_list_); + !biter.done (); + biter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't help much. + if (biter.next ()->grp_id_ == grp_id) + { + ACE_Thread_Descriptor_Base *tdb = biter.advance_and_remove (0); + copy_table[copy_count++] = *tdb; + delete tdb; + } +#endif /* !VXWORKS */ + } + + // Now actually join() with all the threads in this group. + int result = 0; + + for (int i = 0; + i < copy_count && result != -1; + i++) + { + if (ACE_Thread::join (copy_table[i].thr_handle_) == -1) + result = -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + ::pthread_detach (©_table[i].thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + } + + delete [] copy_table; + + return result; +} + +// Must be called when thread goes out of scope to clean up its table +// slot. + +void * +ACE_Thread_Manager::exit (void *status, int do_thr_exit) +{ + ACE_TRACE ("ACE_Thread_Manager::exit"); +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + int close_handle = 0; +#endif /* ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +#if defined (ACE_WIN32) + // Remove detached thread handle. + + if (do_thr_exit) + { +#if 0 + // @@ This callback is now taken care of by TSS_Cleanup. Do we + // need it anymore? + + // On Win32, if we really wants to exit from a thread, we must + // first clean up the thread specific storage. By doing so, + // ACE_Thread_Manager::exit will be called again with + // do_thr_exit = 0 and cleaning up the ACE_Cleanup_Info (but not + // exiting the thread.) After the following call returns, we + // are safe to exit this thread. + delete ACE_Thread_Exit::instance (); +#endif /* 0 */ + ACE_Thread::exit (status); + } +#endif /* ACE_WIN32 */ + +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + ACE_Cleanup_Info cleanup_info; + + // Just hold onto the guard while finding this thread's id and + // copying the exit hook. + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + // Find the thread id, but don't use the cache. It might have been + // deleted already. +#if defined (VXWORKS) + ACE_hthread_t id; + ACE_OS::thr_self (id); + ACE_Thread_Descriptor *td = this->find_hthread (id); +#else /* ! VXWORKS */ + ACE_thread_t id = ACE_OS::thr_self (); + ACE_Thread_Descriptor *td = this->find_thread (id); +#endif /* ! VXWORKS */ + + // Locate thread id. + if (td != 0) + { + // @@ Currently, we have just one hook. This should clearly + // be generalized to support an arbitrary number of hooks. + + if (td->cleanup_info_.cleanup_hook_ != 0) + { + // Copy the hook so that we can call it after releasing + // the guard. + cleanup_info = td->cleanup_info_; + td->cleanup_info_.cleanup_hook_ = 0; + } + +#if !defined (VXWORKS) + // Threads created with THR_DAEMON shouldn't exist here, but + // just to be safe, let's put it here. + + if (ACE_BIT_DISABLED (td->thr_state_, ACE_THR_JOINING)) + if (ACE_BIT_DISABLED (td->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (td->flags_, THR_JOINABLE)) + { + // Mark thread as terminated. + ACE_SET_BITS (td->thr_state_, ACE_THR_TERMINATED); + this->register_as_terminated (td); + // Must copy the information here because td will be "freed" below. + } +#if defined (ACE_WIN32) + else + { + close_handle = 1; + } +#endif /* ACE_WIN32 */ +#endif /* ! VXWORKS */ + + // Remove thread descriptor from the table. + this->remove_thr (td, close_handle); + } + // Release the guard. + } + + // Call the cleanup hook. + if (cleanup_info.cleanup_hook_ != 0) + (*cleanup_info.cleanup_hook_) (cleanup_info.object_, + cleanup_info.param_); +#else /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + // Just hold onto the guard while finding this thread's id and + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + // Find the thread id, but don't use the cache. It might have been + // deleted already. +#if defined (VXWORKS) + ACE_hthread_t id; + ACE_OS::thr_self (id); + ACE_Thread_Descriptor* td = this->find_hthread (id); +#else /* ! VXWORKS */ + ACE_thread_t id = ACE_OS::thr_self (); + ACE_Thread_Descriptor* td = this->find_thread (id); +#endif /* ! VXWORKS */ + if (td != 0) + { + // @@ We call Thread_Descriptor terminate this realize the cleanup + // process itself. + td->terminate(); + } + } + + +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + if (do_thr_exit) + { + ACE_Thread::exit (status); + // On reasonable systems should not return. + // However, due to horrible semantics with Win32 thread-specific + // storage this call can return (don't ask...). + } + + return 0; +} + +// Wait for all the threads to exit. + +int +ACE_Thread_Manager::wait (const ACE_Time_Value *timeout, + int abandon_detached_threads) +{ + ACE_TRACE ("ACE_Thread_Manager::wait"); + +#if defined (ACE_HAS_THREADS) + { + // Just hold onto the guard while waiting. + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (ACE_Object_Manager::shutting_down () != 1) + { + // Program is not shutting down. Perform a normal wait on threads. + if (abandon_detached_threads != 0) + { + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + for (ACE_Double_Linked_List_Iterator + iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_BIT_ENABLED (iter.next ()->flags_, + THR_DETACHED | THR_DAEMON) + && ACE_BIT_DISABLED (iter.next ()->flags_, THR_JOINABLE)) + { + this->thr_to_be_removed_.enqueue_tail (iter.next ()); + ACE_SET_BITS (iter.next ()->thr_state_, ACE_THR_JOINING); + } + + if (! this->thr_to_be_removed_.is_empty ()) + { + ACE_Thread_Descriptor *td; + while (this->thr_to_be_removed_.dequeue_head (td) != -1) + this->remove_thr (td, 1); + } + } + + while (this->thr_list_.size () > 0) + if (this->zero_cond_.wait (timeout) == -1) + return -1; + } + else + // Program is shutting down, no chance to wait on threads. + // Therefore, we'll just remove threads from the list. + this->remove_thr_all (); + // Release the guard, giving other threads a chance to run. + } + +#if !defined (VXWORKS) + // @@ VxWorks doesn't support thr_join (yet.) We are working + //on our implementation. Chorus'es thr_join seems broken. + ACE_Thread_Descriptor_Base *item; + +#if defined (CHORUS) + if (ACE_Object_Manager::shutting_down () != 1) + { +#endif /* CHORUS */ + while ((item = this->terminated_thr_list_.delete_head ()) != 0) + { + if (ACE_BIT_DISABLED (item->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (item->flags_, THR_JOINABLE)) + // Detached handles shouldn't reached here. + ACE_Thread::join (item->thr_handle_); + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without + // THR_DETACHED were detached in ACE_OS::thr_create (). + ::pthread_detach (&item->thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + delete item; + } +#if defined (CHORUS) + } +#endif /* CHORUS */ + +#endif /* ! VXWORKS */ +#else + ACE_UNUSED_ARG (timeout); + ACE_UNUSED_ARG (abandon_detached_threads); +#endif /* ACE_HAS_THREADS */ + + return 0; +} + +int +ACE_Thread_Manager::apply_task (ACE_Task_Base *task, + ACE_THR_MEMBER_FUNC func, + int arg) +{ + ACE_TRACE ("ACE_Thread_Manager::apply_task"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + + int result = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->task_ == task + && (this->*func) (iter.next (), arg) == -1) + result = -1; + + // Must remove threads after we have traversed the thr_list_ to + // prevent clobber thr_list_'s integrity. + + if (! this->thr_to_be_removed_.is_empty ()) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + + for (ACE_Thread_Descriptor *td; + this->thr_to_be_removed_.dequeue_head (td) != -1; + ) + this->remove_thr (td, 1); + } + + return result; +} + +// Wait for all threads to exit a task. + +int +ACE_Thread_Manager::wait_task (ACE_Task_Base *task) +{ + int copy_count = 0; + ACE_Thread_Descriptor_Base *copy_table = 0; + + // We have to make sure that while we wait for these threads to + // exit, we do not have the lock. Therefore we make a copy of all + // interesting entries and let go of the lock. + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + +#if !defined (VXWORKS) + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size () + + this->terminated_thr_list_.size ()], + -1); +#else + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size ()], + -1); +#endif /* VXWORKS */ + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't wait on them here. + if (iter.next ()->task_ == task && + (ACE_BIT_DISABLED (iter.next ()->flags_, + THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (iter.next ()->flags_, + THR_JOINABLE))) + { + ACE_SET_BITS (iter.next ()->thr_state_, + ACE_THR_JOINING); + copy_table[copy_count++] = *iter.next (); + } + +#if !defined (VXWORKS) + for (ACE_Double_Linked_List_Iterator titer (this->terminated_thr_list_); + !titer.done (); + titer.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we can't help much here. + if (titer.next ()->task_ == task) + { + ACE_Thread_Descriptor_Base *tdb = + titer.advance_and_remove (0); + copy_table[copy_count++] = *tdb; + delete tdb; + } +#endif /* VXWORKS */ + } + + // Now to do the actual work + int result = 0; + + for (int i = 0; + i < copy_count && result != -1; + i++) + { + if (ACE_Thread::join (copy_table[i].thr_handle_) == -1) + result = -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + ::pthread_detach (©_table[i].thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + } + + delete [] copy_table; + + return result; +} + +// Suspend a task + +int +ACE_Thread_Manager::suspend_task (ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::suspend_thr)); +} + +// Resume a task. +int +ACE_Thread_Manager::resume_task (ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::resume_thr)); +} + +// Kill a task. + +int +ACE_Thread_Manager::kill_task (ACE_Task_Base *task, int /* signum */) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::kill_thr)); +} + +// Cancel a task. +int +ACE_Thread_Manager::cancel_task (ACE_Task_Base *task, + int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::cancel_thr), + async_cancel); +} + +// Locate the index in the table associated with from the +// beginning of the table up to an index. Must be called with the +// lock held. + +ACE_Thread_Descriptor * +ACE_Thread_Manager::find_task (ACE_Task_Base *task, int slot) +{ + ACE_TRACE ("ACE_Thread_Manager::find_task"); + + int i = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (i >= slot) + break; + + if (task == iter.next ()->task_) + return iter.next (); + + i++; + } + + return 0; +} + +// Returns the number of ACE_Task in a group. + +int +ACE_Thread_Manager::num_tasks_in_group (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::num_tasks_in_group"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int tasks_count = 0; + size_t i = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (iter.next ()->grp_id_ == grp_id + && this->find_task (iter.next ()->task_, i) == 0 + && iter.next ()->task_ != 0) + tasks_count++; + + i++; + } + return tasks_count; +} + +// Returns the number of threads in an ACE_Task. + +int +ACE_Thread_Manager::num_threads_in_task (ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::num_threads_in_task"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int threads_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->task_ == task) + threads_count++; + + return threads_count; +} + +// Returns in task_list a list of ACE_Tasks registered with ACE_Thread_Manager. + +int +ACE_Thread_Manager::task_all_list (ACE_Task_Base *task_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::task_all_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t task_list_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (task_list_count >= n) + break; + + ACE_Task_Base *task_p = iter.next ()->task_; + if (0 != task_p) + { + // This thread has a task pointer; see if it's already in the + // list. Don't add duplicates. + size_t i = 0; + for (; i < task_list_count; ++i) + if (task_list[i] == task_p) + break; + if (i == task_list_count) // No match - add this one + task_list[task_list_count++] = task_p; + } + } + + return task_list_count; +} + +// Returns in thread_list a list of all thread ids + +int +ACE_Thread_Manager::thread_all_list ( ACE_thread_t thread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_all_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t thread_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (thread_count >= n) + break; + + thread_list[thread_count] = iter.next ()->thr_id_; + thread_count ++; + } + + return thread_count; +} + +// Returns in task_list a list of ACE_Tasks in a group. + +int +ACE_Thread_Manager::task_list (int grp_id, + ACE_Task_Base *task_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::task_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_Task_Base **task_list_iterator = task_list; + size_t task_list_count = 0; + size_t i = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (task_list_count >= n) + break; + + if (iter.next ()->grp_id_ == grp_id + && this->find_task (iter.next ()->task_, i) == 0) + { + task_list_iterator[task_list_count] = iter.next ()->task_; + task_list_count++; + } + + i++; + } + + return task_list_count; +} + +// Returns in thread_list a list of thread ids in an ACE_Task. + +int +ACE_Thread_Manager::thread_list (ACE_Task_Base *task, + ACE_thread_t thread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t thread_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (thread_count >= n) + break; + + if (iter.next ()->task_ == task) + { + thread_list[thread_count] = iter.next ()->thr_id_; + thread_count++; + } + } + + return thread_count; +} + +// Returns in thread_list a list of thread handles in an ACE_Task. + +int +ACE_Thread_Manager::hthread_list (ACE_Task_Base *task, + ACE_hthread_t hthread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t hthread_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (hthread_count >= n) + break; + + if (iter.next ()->task_ == task) + { + hthread_list[hthread_count] = iter.next ()->thr_handle_; + hthread_count++; + } + } + + return hthread_count; +} + +int +ACE_Thread_Manager::thread_grp_list (int grp_id, + ACE_thread_t thread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_grp_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t thread_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (thread_count >= n) + break; + + if (iter.next ()->grp_id_ == grp_id) + { + thread_list[thread_count] = iter.next ()->thr_id_; + thread_count++; + } + } + + return thread_count; +} + +// Returns in thread_list a list of thread handles in an ACE_Task. + +int +ACE_Thread_Manager::hthread_grp_list (int grp_id, + ACE_hthread_t hthread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_grp_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t hthread_count = 0; + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (hthread_count >= n) + break; + + if (iter.next ()->grp_id_ == grp_id) + { + hthread_list[hthread_count] = iter.next ()->thr_handle_; + hthread_count++; + } + } + + return hthread_count; +} + +int +ACE_Thread_Manager::set_grp (ACE_Task_Base *task, int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::set_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + for (ACE_Double_Linked_List_Iterator iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->task_ == task) + iter.next ()->grp_id_ = grp_id; + + return 0; +} + +int +ACE_Thread_Manager::get_grp (ACE_Task_Base *task, int &grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::get_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_FIND (this->find_task (task), ptr); + grp_id = ptr->grp_id_; + return 0; +} + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +# if defined (ACE_THREAD_MANAGER_LACKS_STATICS) + template class ACE_Singleton; +# endif /* defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + template class ACE_Auto_Basic_Ptr; + template class auto_ptr; + template class ACE_Double_Linked_List; + template class ACE_Double_Linked_List_Iterator_Base; + template class ACE_Double_Linked_List_Iterator; + template class ACE_Double_Linked_List; + template class ACE_Double_Linked_List_Iterator_Base; + template class ACE_Double_Linked_List_Iterator; + template class ACE_Node; + template class ACE_Unbounded_Queue; + template class ACE_Unbounded_Queue_Iterator; + template class ACE_Free_List; + template class ACE_Locked_Free_List; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +# if defined (ACE_THREAD_MANAGER_LACKS_STATICS) + #pragma instantiate ACE_Singleton +# endif /* defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + #pragma instantiate ACE_Auto_Basic_Ptr + #pragma instantiate auto_ptr + #pragma instantiate ACE_Double_Linked_List + #pragma instantiate ACE_Double_Linked_List_Iterator_Base + #pragma instantiate ACE_Double_Linked_List_Iterator + #pragma instantiate ACE_Double_Linked_List + #pragma instantiate ACE_Double_Linked_List_Iterator_Base + #pragma instantiate ACE_Double_Linked_List_Iterator + #pragma instantiate ACE_Node + #pragma instantiate ACE_Unbounded_Queue + #pragma instantiate ACE_Unbounded_Queue_Iterator + #pragma instantiate ACE_Free_List + #pragma instantiate ACE_Locked_Free_List +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Threads/Thread_Manager.h b/ace/Threads/Thread_Manager.h new file mode 100644 index 00000000000..e2c6d190db1 --- /dev/null +++ b/ace/Threads/Thread_Manager.h @@ -0,0 +1,1008 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Thread_Manager.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_THREAD_MANAGER_H +#define ACE_THREAD_MANAGER_H +#include "ace/pre.h" + +#include "ace/Thread.h" +#include "ace/Thread_Adapter.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Synch.h" +#include "ace/Unbounded_Queue.h" +#include "ace/Containers.h" +#include "ace/Free_List.h" +#include "ace/Singleton.h" +#include "ace/Log_Msg.h" + +// The following macros control how a Thread Manager manages a pool of +// Thread_Descriptor. Currently, the default behavior is not to +// preallocate any thread descriptor and never (well, almost never) +// free up any thread descriptor until the Thread Manager gets +// destructed. Which means, once your system is stable, you rarely +// need to pay the price of memory allocation. On a deterministic +// system, which means, the number of threads spawned can be +// determined before hand, you can either redefine the memory pool +// size macros to suit your need or constructed the Thread_Manager +// accordingly. That way, you don't pay the price of memory +// allocation when the system is really doing its job. OTOH, on +// system with resources constraint, you may want to lower the size of +// ACE_DEFAULT_THREAD_MANAGER_HWM to avoid unused memory hanging +// around. + +#if !defined (ACE_DEFAULT_THREAD_MANAGER_PREALLOC) +# define ACE_DEFAULT_THREAD_MANAGER_PREALLOC 0 +#endif /* ACE_DEFAULT_THREAD_MANAGER_PREALLOC */ + +#if !defined (ACE_DEFAULT_THREAD_MANAGER_LWM) +# define ACE_DEFAULT_THREAD_MANAGER_LWM 1 +#endif /* ACE_DEFAULT_THREAD_MANAGER_LWM */ + +#if !defined (ACE_DEFAULT_THREAD_MANAGER_INC) +# define ACE_DEFAULT_THREAD_MANAGER_INC 1 +#endif /* ACE_DEFAULT_THREAD_MANAGER_INC */ + +#if !defined (ACE_DEFAULT_THREAD_MANAGER_HWM) +# define ACE_DEFAULT_THREAD_MANAGER_HWM ACE_DEFAULT_FREE_LIST_HWM +// this is a big number +#endif /* ACE_DEFAULT_THREAD_MANAGER_HWM */ + +// This is the synchronization mechanism used to prevent a thread +// descriptor gets removed from the Thread_Manager before it gets +// stash into it. If you want to disable this feature (and risk of +// corrupting the freelist,) you define the lock as ACE_Null_Mutex. +// Usually, if you can be sure that your threads will run for an +// extended period of time, you can safely disable the lock. + +#if !defined (ACE_DEFAULT_THREAD_MANAGER_LOCK) +# define ACE_DEFAULT_THREAD_MANAGER_LOCK ACE_SYNCH_MUTEX +#endif /* ACE_DEFAULT_THREAD_MANAGER_LOCK */ + +// Forward declarations. +class ACE_Task_Base; +class ACE_Thread_Manager; +class ACE_Thread_Descriptor; + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + /** + * @class ACE_At_Thread_Exit + * + * @brief Contains a method to be applied when a thread is terminated. + */ +class ACE_Export ACE_At_Thread_Exit +{ + friend class ACE_Thread_Descriptor; + friend class ACE_Thread_Manager; +public: + // Default constructor + ACE_At_Thread_Exit (void); + + // The destructor + virtual ~ACE_At_Thread_Exit (void); + + // has the ownership? + int is_owner (void) const; + + // Set the ownership of the . + int is_owner (int owner); + + // This was applied? + int was_applied (void) const; + + // Set applied state of . + int was_applied (int applied); + +protected: + /// The next hook in the list. + ACE_At_Thread_Exit *next_; + + // Do the apply if necessary + void do_apply (void); + + /// The apply method. + virtual void apply (void) = 0; + + /// The Thread_Descriptor where this at is registered. + ACE_Thread_Descriptor* td_; + + /// The at was applied? + int was_applied_; + + /// The at has the ownership of this? + int is_owner_; +}; + +class ACE_Export ACE_At_Thread_Exit_Func : public ACE_At_Thread_Exit +{ +public: + // Constructor + ACE_At_Thread_Exit_Func (void *object, + ACE_CLEANUP_FUNC func, + void *param = 0); + + virtual ~ACE_At_Thread_Exit_Func (void); + +protected: + /// The object to be cleanup + void *object_; + + /// The cleanup func + ACE_CLEANUP_FUNC func_; + + /// A param if required + void *param_; + + // The apply method + void apply (void); +}; + +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +/** + * @class ACE_Thread_Descriptor_Base + * + * @brief Basic information for thread descriptors. These information + * gets extracted out because we need it after a thread is + * terminated. + */ +class ACE_Export ACE_Thread_Descriptor_Base : public ACE_OS_Thread_Descriptor +{ + + friend class ACE_Thread_Manager; + friend class ACE_Double_Linked_List; + friend class ACE_Double_Linked_List_Iterator_Base; + friend class ACE_Double_Linked_List_Iterator; + friend class ACE_Double_Linked_List; + friend class ACE_Double_Linked_List_Iterator_Base; + friend class ACE_Double_Linked_List_Iterator; +public: + ACE_Thread_Descriptor_Base (void); + ~ACE_Thread_Descriptor_Base (void); + + // = We need the following operators to make Borland happy. + + /// Equality operator. + int operator== (const ACE_Thread_Descriptor_Base &rhs) const; + + /// Inequality operator. + int operator!= (const ACE_Thread_Descriptor_Base &rhs) const; + + /// Group ID. + int grp_id (void) const; + + /// Current state of the thread. + ACE_UINT32 state (void) const; + + /// Return the pointer to an or NULL if there's no + /// associated with this thread.; + ACE_Task_Base *task (void) const; + +protected: + /// Reset this base thread descriptor. + void reset (void); + + /// Unique thread ID. + ACE_thread_t thr_id_; + + /// Unique handle to thread (used by Win32 and AIX). + ACE_hthread_t thr_handle_; + + /// Group ID. + int grp_id_; + + /// Current state of the thread. + ACE_UINT32 thr_state_; + + /// Pointer to an or NULL if there's no + /// . + ACE_Task_Base *task_; + + /// We need these pointers to maintain the double-linked list in a + /// thread managers. + ACE_Thread_Descriptor_Base *next_; + ACE_Thread_Descriptor_Base *prev_; +}; + +/** + * @class ACE_Thread_Descriptor + * + * @brief Information for controlling threads that run under the control + * of the . + */ +class ACE_Export ACE_Thread_Descriptor : public ACE_Thread_Descriptor_Base +{ +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + friend class ACE_At_Thread_Exit; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + friend class ACE_Thread_Manager; + friend class ACE_Double_Linked_List; + friend class ACE_Double_Linked_List_Iterator; +public: + // = Initialization method. + ACE_Thread_Descriptor (void); + + // = Accessor methods. + /// Unique thread id. + ACE_thread_t self (void) const; + + /// Unique handle to thread (used by Win32 and AIX). + void self (ACE_hthread_t &); + + /// Dump the state of an object. + void dump (void) const; + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + /** + * This cleanup function must be called only for ACE_TSS_cleanup. + * The ACE_TSS_cleanup delegate Log_Msg instance destruction when + * Log_Msg cleanup is called before terminate. + */ + void log_msg_cleanup(ACE_Log_Msg* log_msg); + + /** + * Register an At_Thread_Exit hook and the ownership is acquire by + * Thread_Descriptor, this is the usual case when the AT is dynamically + * allocated. + */ + int at_exit (ACE_At_Thread_Exit* cleanup); + + /// Register an At_Thread_Exit hook and the ownership is retained for the + /// caller. Normally used when the at_exit hook is created in stack. + int at_exit (ACE_At_Thread_Exit& cleanup); +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + /** + * Register an object (or array) for cleanup at thread termination. + * "cleanup_hook" points to a (global, or static member) function + * that is called for the object or array when it to be destroyed. + * It may perform any necessary cleanup specific for that object or + * its class. "param" is passed as the second parameter to the + * "cleanup_hook" function; the first parameter is the object (or + * array) to be destroyed. Returns 0 on success, non-zero on + * failure: -1 if virtual memory is exhausted or 1 if the object (or + * arrayt) had already been registered. + */ + int at_exit (void *object, + ACE_CLEANUP_FUNC cleanup_hook, + void *param); + + /// Do nothing destructor to keep some compilers happy + ~ACE_Thread_Descriptor (void); + + /** + * Do nothing but to acquire the thread descriptor's lock and + * release. This will first check if the thread is registered or + * not. If it is already registered, there's no need to reacquire + * the lock again. This is used mainly to get newly spawned thread + * in synch with thread manager and prevent it from accessing its + * thread descriptor before it gets fully built. This function is + * only called from ACE_Log_Msg::thr_desc. + */ + void acquire_release (void); + + /** + * Set/get the pointer. These are required by the + * ACE_Free_List. ACE_INLINE is specified here because one version + * of g++ couldn't grok this code without it. + */ + ACE_INLINE_FOR_GNUC void set_next (ACE_Thread_Descriptor *td); + ACE_INLINE_FOR_GNUC ACE_Thread_Descriptor *get_next (void) const; + +private: + /// Reset this thread descriptor. + void reset (ACE_Thread_Manager *tm); + +#if !defined (ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + /// Pop an At_Thread_Exit from at thread termination list, apply the at + /// if apply is true. + void at_pop (int apply = 1); + + /// Push an At_Thread_Exit to at thread termination list and set the + /// ownership of at. + void at_push (ACE_At_Thread_Exit* cleanup, + int is_owner = 0); + + /// Run the AT_Thread_Exit hooks. + void do_at_exit (void); + + /// terminate realize the cleanup process to thread termination + void terminate (void); + + /// Thread_Descriptor is the ownership of ACE_Log_Msg if log_msg_!=0 + /// This can occur because ACE_TSS_cleanup was executed before terminate. + ACE_Log_Msg *log_msg_; + + /// The AT_Thread_Exit list + ACE_At_Thread_Exit *at_exit_list_; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + /** + * Stores the cleanup info for a thread. + * @@ Note, this should be generalized to be a stack of + * s. + */ + ACE_Cleanup_Info cleanup_info_; + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + /// Pointer to an or NULL if there's no + /// . + ACE_Thread_Manager* tm_; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + /// Registration lock to prevent premature removal of thread descriptor. + ACE_DEFAULT_THREAD_MANAGER_LOCK *sync_; + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + /// Keep track of termination status. + int terminated_; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ +}; + +// Forward declaration. +class ACE_Thread_Control; + +// This typedef should be (and used to be) inside the +// ACE_Thread_Manager declaration. But, it caused compilation +// problems on g++/VxWorks/i960 with -g. Note that +// ACE_Thread_Manager::THR_FUNC is only used internally in +// ACE_Thread_Manager, so it's not useful for anyone else. +// It also caused problems on IRIX5 with g++. +#if defined (__GNUG__) +typedef int (ACE_Thread_Manager::*ACE_THR_MEMBER_FUNC)(ACE_Thread_Descriptor *, int); +#endif /* __GNUG__ */ + +/** + * @class ACE_Thread_Manager + * + * @brief Manages a pool of threads. + * + * This class allows operations on groups of threads atomically. + * The default behavior of thread manager is to wait on + * all threads under it's management when it gets destructed. + * Therefore, remember to remove a thread from thread manager if + * you don't want it to wait for the thread. There are also + * function to disable this default wait-on-exit behavior. + * However, if your program depends on turning this off to run + * correctly, you are probably doing something wrong. Rule of + * thumb, use ACE_Thread to manage your daemon threads. + * Notice that if there're threads live beyond the scope of + *
, you are sure to have resource leaks in your program. + * Remember to wait on threads before exiting
if that + * could happen in your programs. + */ +class ACE_Export ACE_Thread_Manager +{ +public: + friend class ACE_Thread_Control; +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + friend class ACE_Thread_Descriptor; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +#if !defined (__GNUG__) + typedef int (ACE_Thread_Manager::*ACE_THR_MEMBER_FUNC)(ACE_Thread_Descriptor *, int); +#endif /* !__GNUG__ */ + + // These are the various states a thread managed by the + // can be in. + enum + { + /// Uninitialized. + ACE_THR_IDLE = 0x00000000, + + /// Created but not yet running. + ACE_THR_SPAWNED = 0x00000001, + + /// Thread is active (naturally, we don't know if it's actually + /// *running* because we aren't the scheduler...). + ACE_THR_RUNNING = 0x00000002, + + /// Thread is suspended. + ACE_THR_SUSPENDED = 0x00000004, + + /// Thread has been cancelled (which is an indiction that it needs to + /// terminate...). + ACE_THR_CANCELLED = 0x00000008, + + /// Thread has shutdown, but the slot in the thread manager hasn't + /// been reclaimed yet. + ACE_THR_TERMINATED = 0x00000010, + + /// Join operation has been invoked on the thread by thread manager. + ACE_THR_JOINING = 0x10000000 + }; + + // = Initialization and termination methods. + /** + * @breif Initialization and termination methods. + * + * Internally, ACE_Thread_Manager keeps a freelist for caching + * resources it uses to keep track of managed threads (not the + * threads themselves.) @a prealloc, @a lwm, @a inc, @hwm + * determine the initial size, the low water mark, increment step, + * and high water mark of the freelist. + * + * @sa ACE_Free_List + */ + ACE_Thread_Manager (size_t preaolloc = ACE_DEFAULT_THREAD_MANAGER_PREALLOC, + size_t lwm = ACE_DEFAULT_THREAD_MANAGER_LWM, + size_t inc = ACE_DEFAULT_THREAD_MANAGER_INC, + size_t hwm = ACE_DEFAULT_THREAD_MANAGER_HWM); + virtual ~ACE_Thread_Manager (void); + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) + /// Get pointer to a process-wide . + static ACE_Thread_Manager *instance (void); + + /// Set pointer to a process-wide and return + /// existing pointer. + static ACE_Thread_Manager *instance (ACE_Thread_Manager *); + + /// Delete the dynamically allocated Singleton + static void close_singleton (void); +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + + /// No-op. Currently unused. + int open (size_t size = 0); + + /** + * Release all resources. + * By default, this method will wait till all threads + * exit. However, when called from , most global resources + * are destroyed and thus, we don't try to wait but just clean up the thread + * descriptor list. + */ + int close (void); + + // The * argument to each of the family member + // functions is interpreted and used as shown in the following + // table. NOTE: the final option, to provide task names, is _only_ + // supported on VxWorks! + // + // Value of ACE_thread_t * argument Use Platforms + // ================================ ========================== ========= + // 0 Not used. All + // non-0 (and points to 0 char * The task name is passed All + // on VxWorks) back in the char *. + // non-0, points to non-0 char * The char * is used as VxWorks only + // the task name. The + // argument is not modified. + + /** + * Create a new thread, which executes . + * Returns: on success a unique group id that can be used to control + * other threads added to the same group. On failure, returns -1. + */ + int spawn (ACE_THR_FUNC func, + void *args = 0, + long flags = THR_NEW_LWP | THR_JOINABLE, + ACE_thread_t * = 0, + ACE_hthread_t *t_handle = 0, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + int grp_id = -1, + void *stack = 0, + size_t stack_size = 0); + + /** + * Create N new threads, all of which execute . + * Returns: on success a unique group id that can be used to control + * all of the threads in the same group. On failure, returns -1. + */ + int spawn_n (size_t n, + ACE_THR_FUNC func, + void *args = 0, + long flags = THR_NEW_LWP | THR_JOINABLE, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + int grp_id = -1, + ACE_Task_Base *task = 0, + ACE_hthread_t thread_handles[] = 0, + void *stack[] = 0, + size_t stack_size[] = 0); + + /** + * Spawn N new threads, which execute with argument . + * If != 0 the thread_ids of successfully spawned + * threads will be placed into the buffer (which must + * be the same size as ). If != 0 it is assumed to be an + * array of pointers to the base of the stacks to use for the + * threads being spawned. If != 0 it is assumed to be + * an array of values indicating how big each of the + * corresponding s are. If != 0 it is + * assumed to be an array of thread_handles that will be + * assigned the values of the thread handles being spawned. Returns + * -1 on failure ( will explain...), otherwise returns the + * group id of the threads. + */ + int spawn_n (ACE_thread_t thread_ids[], + size_t n, + ACE_THR_FUNC func, + void *args, + long flags, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + int grp_id = -1, + void *stack[] = 0, + size_t stack_size[] = 0, + ACE_hthread_t thread_handles[] = 0, + ACE_Task_Base *task = 0); + + /** + * Called to clean up when a thread exits. If is + * non-0 then is called to exit the thread, in + * which case is passed as the exit value of the thread. + * Should _not_ be called by main thread. + */ + void *exit (void *status = 0, + int do_thread_exit = 1); + + /** + * Block until there are no more threads running in the + * or expires. Note that is + * treated as "absolute" time. Returns 0 on success and -1 on + * failure. If is set, wait will first + * check thru its thread list for threads with THR_DETACHED or + * THR_DAEMON flags set and remove these threads. Notice that + * unlike other wait_* function, by default, does wait on + * all thread spawned by this thread_manager no matter the detached + * flags are set or not unless it is called with + * flag set. + * NOTE that if this function is called while the ACE_Object_Manager + * is shutting down (as a result of program rundown via ACE::fini), + * it will not wait for any threads to complete. If you must wait for + * threads spawned by this thread manager to complete and you are in a + * ACE rundown situation (such as your object is being destroyed by the + * ACE_Object_Manager) you can use wait_grp instead. + */ + int wait (const ACE_Time_Value *timeout = 0, + int abandon_detached_threads = 0); + + /// Join a thread specified by . Do not wait on a detached thread. + int join (ACE_thread_t tid, void **status = 0); + + /** + * Block until there are no more threads running in a group. + * Returns 0 on success and -1 on failure. Notice that wait_grp + * will not wait on detached threads. + */ + int wait_grp (int grp_id); + + // = Accessors for ACE_Thread_Descriptors. + /** + * Get a pointer to the calling thread's own thread_descriptor. + * This must be called from a spawn thread. This function will + * fetch the info from TSS. + */ + ACE_Thread_Descriptor *thread_desc_self (void); + + /// Return a pointer to the thread's Thread_Descriptor, + /// 0 if fail. + ACE_Thread_Descriptor *thread_descriptor (ACE_thread_t); + + /// Return a pointer to the thread's Thread_Descriptor, + /// 0 if fail. + ACE_Thread_Descriptor *hthread_descriptor (ACE_hthread_t); + + /** + * Return the "real" handle to the calling thread, caching it if + * necessary in TSS to speed up subsequent lookups. This is + * necessary since on some platforms (e.g., Win32) we can't get this + * handle via direct method calls. Notice that you should *not* + * close the handle passed back from this method. It is used + * internally by Thread Manager. On the other hand, you *have to* + * use this internal thread handle when working on Thread_Manager. + * Return -1 if fail. + */ + int thr_self (ACE_hthread_t &); + + /** + * Return the unique ID of the thread. This is not strictly + * necessary (because a thread can always just call + * ). However, we put it here to be complete. + */ + ACE_thread_t thr_self (void); + + /** + * Returns a pointer to the current we're executing + * in if this thread is indeed running in an , else + * return 0. + */ + ACE_Task_Base *task (void); + + // = Suspend methods, which isn't supported on POSIX pthreads (will not block). + /** + * Suspend all threads + * Suspend a single thread. + * Suspend a group of threads. + * True if is inactive (i.e., suspended), else false. + */ + int suspend_all (void); + int suspend (ACE_thread_t); + int suspend_grp (int grp_id); + int testsuspend (ACE_thread_t t_id); + + // = Resume methods, which isn't supported on POSIX pthreads (will not block). + /** + * Resume all stopped threads + * Resume a single thread. + * Resume a group of threads. + * True if is active (i.e., resumed), else false. + */ + int resume_all (void); + int resume (ACE_thread_t); + int resume_grp (int grp_id); + int testresume (ACE_thread_t t_id); + + // = Send signals to one or more threads without blocking. + /** + * Send to all stopped threads. Not supported on platforms + * that do not have advanced signal support, such as Win32. + * Send the to a single thread. Not supported on platforms + * that do not have advanced signal support, such as Win32. + * Send to a group of threads, not supported on platforms + * that do not have advanced signal support, such as Win32. + */ + int kill_all (int signum); + int kill (ACE_thread_t, + int signum); + int kill_grp (int grp_id, + int signum); + + // = Cancel methods, which provides a cooperative thread-termination mechanism (will not block). + /** + * Cancel's all the threads. + * Cancel a single thread. + * Cancel a group of threads. + * True if is cancelled, else false. + */ + int cancel_all (int async_cancel = 0); + int cancel (ACE_thread_t, int async_cancel = 0); + int cancel_grp (int grp_id, int async_cancel = 0); + int testcancel (ACE_thread_t t_id); + + // = Set/get group ids for a particular thread id. + int set_grp (ACE_thread_t, + int grp_id); + int get_grp (ACE_thread_t, + int &grp_id); + + // = The following methods are new methods which resemble current + // methods in . For example, the + // method resembles the method, and + // resembles . + + // = Operations on ACE_Tasks. + + /** + * Block until there are no more threads running in . Returns + * 0 on success and -1 on failure. Note that will not + * wait on detached threads. + * Suspend all threads in an ACE_Task. + * Resume all threads in an ACE_Task. + * Send a signal to all threads in an . + */ + int wait_task (ACE_Task_Base *task); + int suspend_task (ACE_Task_Base *task); + int resume_task (ACE_Task_Base *task); + int kill_task (ACE_Task_Base *task, + int signum); + + /** + * Cancel all threads in an . If is non-0, + * then asynchronously cancel these threads if the OS platform + * supports cancellation. Otherwise, perform a "cooperative" + * cancellation. + */ + int cancel_task (ACE_Task_Base *task, int async_cancel = 0); + + // = Collect thread handles in the thread manager. Notice that + // the collected information is just a snapshot. + /// Check if the thread is managed by the thread manager. Return true if + /// the thread is found, false otherwise. + int hthread_within (ACE_hthread_t handle); + int thread_within (ACE_thread_t tid); + + /// Returns the number of in a group. + int num_tasks_in_group (int grp_id); + + /// Returns the number of threads in an . + int num_threads_in_task (ACE_Task_Base *task); + + /** + * Returns in a list of up to in a + * group. The caller must allocate the memory for . In + * case of an error, -1 is returned. If no requested values are + * found, 0 is returned, otherwise correct number of retrieved + * values are returned. + */ + int task_list (int grp_id, + ACE_Task_Base *task_list[], + size_t n); + + /** + * Returns in a list of up to thread ids in an + * . The caller must allocate the memory for + * . In case of an error, -1 is returned. If no + * requested values are found, 0 is returned, otherwise correct + * number of retrieved values are returned. + */ + int thread_list (ACE_Task_Base *task, + ACE_thread_t thread_list[], + size_t n); + + /** + * Returns in a list of up to thread handles in + * an . The caller must allocate memory for + * . In case of an error, -1 is returned. If no + * requested values are found, 0 is returned, otherwise correct + * number of retrieved values are returned. + */ + int hthread_list (ACE_Task_Base *task, + ACE_hthread_t hthread_list[], + size_t n); + + /** + * Returns in a list of up to thread ids in a + * group . The caller must allocate the memory for + * . In case of an error, -1 is returned. If no + * requested values are found, 0 is returned, otherwise correct + * number of retrieved values are returned. + */ + int thread_grp_list (int grp_id, + ACE_thread_t thread_list[], + size_t n); + + /** + * Returns in a list of up to thread handles in + * a group . The caller must allocate memory for + * . + */ + int hthread_grp_list (int grp_id, + ACE_hthread_t hthread_list[], + size_t n); + + /** + * Returns in a list of up to . The + * caller must allocate the memory for . In case of an + * error, -1 is returned. If no requested values are found, 0 is + * returned, otherwise correct number of retrieved values are + * returned. + */ + int task_all_list (ACE_Task_Base *task_list[], + size_t n); + + /** + * Returns in a list of up to thread ids. The + * caller must allocate the memory for . In case of an + * error, -1 is returned. If no requested values are found, 0 is + * returned, otherwise correct number of retrieved values are + * returned. + */ + int thread_all_list (ACE_thread_t thread_list[], + size_t n); + + // = Set/get group ids for a particular task. + int set_grp (ACE_Task_Base *task, int grp_id); + int get_grp (ACE_Task_Base *task, int &grp_id); + + /// Return a count of the current number of threads active in the + /// . + int count_threads (void) const; + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + /** + * Register an At_Thread_Exit hook and the ownership is acquire by + * Thread_Descriptor, this is the usual case when the AT is dynamically + * allocated. + */ + int at_exit (ACE_At_Thread_Exit* cleanup); + + /// Register an At_Thread_Exit hook and the ownership is retained for the + /// caller. Normally used when the at_exit hook is created in stack. + int at_exit (ACE_At_Thread_Exit& cleanup); +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + /** + * + ***** + * @deprecated This function is deprecated. Please use the previous two + * at_exit method. Notice that you should avoid mixing this method + * with the previous two at_exit methods. + ***** + * + * Register an object (or array) for cleanup at + * thread termination. "cleanup_hook" points to a (global, or + * static member) function that is called for the object or array + * when it to be destroyed. It may perform any necessary cleanup + * specific for that object or its class. "param" is passed as the + * second parameter to the "cleanup_hook" function; the first + * parameter is the object (or array) to be destroyed. + * "cleanup_hook", for example, may delete the object (or array). + * If == 0, the will _NOT_ get cleanup at + * thread exit. You can use this to cancel the previously added + * at_exit. + */ + int at_exit (void *object, + ACE_CLEANUP_FUNC cleanup_hook, + void *param); + + /// Access function to determine whether the Thread_Manager will + /// wait for its thread to exit or not when being closing down. + void wait_on_exit (int dowait); + int wait_on_exit (void); + + /// Dump the state of an object. + void dump (void); + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + /// Create a new thread (must be called with locks held). + virtual int spawn_i (ACE_THR_FUNC func, + void *args, + long flags, + ACE_thread_t * = 0, + ACE_hthread_t *t_handle = 0, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + int grp_id = -1, + void *stack = 0, + size_t stack_size = 0, + ACE_Task_Base *task = 0); + + /// Run the registered hooks when the thread exits. + void run_thread_exit_hooks (int i); + + /// Locate the index of the table slot occupied by . Returns + /// -1 if is not in the table doesn't contain . + ACE_Thread_Descriptor *find_thread (ACE_thread_t t_id); + + /// Locate the index of the table slot occupied by . Returns + /// -1 if is not in the table doesn't contain . + ACE_Thread_Descriptor *find_hthread (ACE_hthread_t h_id); + + /** + * Locate the thread descriptor address of the list occupied by + * . Returns 0 if is not in the table doesn't contain + * . + */ + ACE_Thread_Descriptor *find_task (ACE_Task_Base *task, + int slot = -1); + + /// Insert a thread in the table (checks for duplicates). + int insert_thr (ACE_thread_t t_id, + ACE_hthread_t, + int grp_id = -1, + long flags = 0); + + /// Append a thread in the table (adds at the end, growing the table + /// if necessary). + int append_thr (ACE_thread_t t_id, ACE_hthread_t, + ACE_UINT32, + int grp_id, + ACE_Task_Base *task = 0, + long flags = 0, + ACE_Thread_Descriptor *td = 0); + + /// Remove thread from the table. + void remove_thr (ACE_Thread_Descriptor *td, + int close_handler); + + /// Remove all threads from the table. + void remove_thr_all (void); + + // = The following four methods implement a simple scheme for + // operating on a collection of threads atomically. + + /** + * Efficiently check whether is in a particular . + * This call updates the TSS cache if possible to speed up + * subsequent searches. + */ + int check_state (ACE_UINT32 state, + ACE_thread_t thread, + int enable = 1); + + /// Apply to all members of the table that match the + int apply_task (ACE_Task_Base *task, + ACE_THR_MEMBER_FUNC, + int = 0); + + /// Apply to all members of the table that match the . + int apply_grp (int grp_id, + ACE_THR_MEMBER_FUNC func, + int arg = 0); + + /// Apply to all members of the table. + int apply_all (ACE_THR_MEMBER_FUNC, + int = 0); + + /// Join the thread described in . + int join_thr (ACE_Thread_Descriptor *td, + int = 0); + + /// Resume the thread described in . + int resume_thr (ACE_Thread_Descriptor *td, + int = 0); + + /// Suspend the thread described in . + int suspend_thr (ACE_Thread_Descriptor *td, + int = 0); + + /// Send signal to the thread described in . + int kill_thr (ACE_Thread_Descriptor *td, + int signum); + + /// Set the cancellation flag for the thread described in . + int cancel_thr (ACE_Thread_Descriptor *td, + int async_cancel = 0); + + /// Register a thread as terminated and put it into the . + int register_as_terminated (ACE_Thread_Descriptor *td); + + /** + * Keeping a list of thread descriptors within the thread manager. + * Double-linked list enables us to cache the entries in TSS + * and adding/removing thread descriptor entries without + * affecting other thread's descriptor entries. + */ + ACE_Double_Linked_List thr_list_; + +#if !defined (VXWORKS) + /// Collect terminated but not yet joined thread entries. + ACE_Double_Linked_List terminated_thr_list_; +#endif /* VXWORKS */ + + /// Collect pointers to thread descriptors of threads to be removed later. + ACE_Unbounded_Queue thr_to_be_removed_; + + /// Keeps track of the next group id to assign. + int grp_id_; + + /// Set if we want the Thread_Manager to wait on all threads before + /// being closed, reset otherwise. + int automatic_wait_; + + // = ACE_Thread_Mutex and condition variable for synchronizing termination. +#if defined (ACE_HAS_THREADS) + /// Serialize access to the . + ACE_Thread_Mutex lock_; + + /// Keep track of when there are no more threads. + ACE_Condition_Thread_Mutex zero_cond_; +#endif /* ACE_HAS_THREADS */ + +private: + ACE_Locked_Free_List thread_desc_freelist_; + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) + /// Pointer to a process-wide . + static ACE_Thread_Manager *thr_mgr_; + + /// Must delete the if non-0. + static int delete_thr_mgr_; +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ +}; + +#if defined (ACE_THREAD_MANAGER_LACKS_STATICS) +#define ACE_THREAD_MANAGER_SINGLETON_DEFINE \ + ACE_Singleton; +typedef ACE_Singleton ACE_THREAD_MANAGER_SINGLETON; +#endif /* defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + +#if defined (__ACE_INLINE__) +#include "ace/Thread_Manager.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_THREAD_MANAGER_H */ diff --git a/ace/Threads/Thread_Manager.i b/ace/Threads/Thread_Manager.i new file mode 100644 index 00000000000..36b808b7e9c --- /dev/null +++ b/ace/Threads/Thread_Manager.i @@ -0,0 +1,322 @@ +/* -*- C++ -*- */ +// $Id$ + +// Thread_Manager.i + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) +ACE_INLINE +ACE_At_Thread_Exit::ACE_At_Thread_Exit (void) + : next_ (0), + td_ (0), + was_applied_ (0), + is_owner_ (1) +{ +} + +ACE_INLINE int +ACE_At_Thread_Exit::was_applied() const + +{ + return was_applied_; +} + +ACE_INLINE int +ACE_At_Thread_Exit::was_applied (int applied) +{ + was_applied_ = applied; + if (was_applied_) + td_ = 0; + return was_applied_; +} + +ACE_INLINE int +ACE_At_Thread_Exit::is_owner() const +{ + return is_owner_; +} + +ACE_INLINE int +ACE_At_Thread_Exit::is_owner (int owner) +{ + is_owner_ = owner; + return is_owner_; +} + +ACE_INLINE void +ACE_At_Thread_Exit::do_apply (void) +{ + if (!this->was_applied_ && this->is_owner_) + td_->at_pop(); +} + +ACE_INLINE +ACE_At_Thread_Exit::~ACE_At_Thread_Exit (void) +{ + this->do_apply (); +} + +ACE_INLINE +ACE_At_Thread_Exit_Func::ACE_At_Thread_Exit_Func (void *object, + ACE_CLEANUP_FUNC func, + void *param) + : object_(object), + func_(func), + param_(param) +{ +} + +ACE_INLINE +ACE_At_Thread_Exit_Func::~ACE_At_Thread_Exit_Func (void) +{ + this->do_apply (); +} + +ACE_INLINE void +ACE_At_Thread_Exit_Func::apply () +{ + func_ (object_, param_); +} +#endif /* ! ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +ACE_INLINE +ACE_Thread_Descriptor_Base::ACE_Thread_Descriptor_Base (void) + : ACE_OS_Thread_Descriptor (), + thr_id_ (ACE_OS::NULL_thread), + thr_handle_ (ACE_OS::NULL_hthread), + grp_id_ (0), + thr_state_ (ACE_Thread_Manager::ACE_THR_IDLE), + task_ (0), + next_ (0), + prev_ (0) +{ +} + +ACE_INLINE +ACE_Thread_Descriptor_Base::~ACE_Thread_Descriptor_Base (void) +{ +} + +ACE_INLINE int +ACE_Thread_Descriptor_Base::operator==(const ACE_Thread_Descriptor_Base &rhs) const +{ + return ACE_OS::thr_cmp (this->thr_handle_, rhs.thr_handle_) == 0 + && ACE_OS::thr_equal (this->thr_id_, rhs.thr_id_) == 0; +} + +ACE_INLINE int +ACE_Thread_Descriptor_Base::operator!=(const ACE_Thread_Descriptor_Base &rhs) const +{ + return !(*this == rhs); +} + +ACE_INLINE ACE_Task_Base * +ACE_Thread_Descriptor_Base::task (void) const +{ + ACE_TRACE ("ACE_Thread_Descriptor_Base::task"); + return this->task_; +} + +// Group ID. + +ACE_INLINE int +ACE_Thread_Descriptor_Base::grp_id (void) const +{ + ACE_TRACE ("ACE_Thread_Descriptor_Base::grp_id"); + return grp_id_; +} + +// Current state of the thread. +ACE_INLINE ACE_UINT32 +ACE_Thread_Descriptor_Base::state (void) const +{ + ACE_TRACE ("ACE_Thread_Descriptor_Base::state"); + return thr_state_; +} + +// Reset this base descriptor. +ACE_INLINE void +ACE_Thread_Descriptor_Base::reset (void) +{ + ACE_TRACE ("ACE_Thread_Descriptor_Base::reset"); + this->thr_id_ = ACE_OS::NULL_thread; + this->thr_handle_ = ACE_OS::NULL_hthread; + this->grp_id_ = 0; + this->thr_state_ = ACE_Thread_Manager::ACE_THR_IDLE; + this->task_ = 0; + this->flags_ = 0; +} + +// Unique thread id. +ACE_INLINE ACE_thread_t +ACE_Thread_Descriptor::self (void) const +{ + ACE_TRACE ("ACE_Thread_Descriptor::self"); + return this->thr_id_; +} + +// Unique kernel-level thread handle. + +ACE_INLINE void +ACE_Thread_Descriptor::self (ACE_hthread_t &handle) +{ + ACE_TRACE ("ACE_Thread_Descriptor::self"); + handle = this->thr_handle_; +} + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) +ACE_INLINE void +ACE_Thread_Descriptor::log_msg_cleanup (ACE_Log_Msg* log_msg) + +{ + log_msg_ = log_msg; +} +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +// Set the pointer +ACE_INLINE void +ACE_Thread_Descriptor::set_next (ACE_Thread_Descriptor *td) +{ + ACE_TRACE ("ACE_Thread_Descriptor::set_next"); + this->next_ = td; +} + +// Get the pointer +ACE_INLINE ACE_Thread_Descriptor * +ACE_Thread_Descriptor::get_next (void) const +{ + ACE_TRACE ("ACE_Thread_Descriptor::get_next"); + return ACE_static_cast (ACE_Thread_Descriptor * ACE_CAST_CONST, + this->next_); +} + +// Reset this thread descriptor +ACE_INLINE void +ACE_Thread_Descriptor::reset (ACE_Thread_Manager *tm) +{ + ACE_TRACE ("ACE_Thread_Descriptor::reset"); + this->ACE_Thread_Descriptor_Base::reset (); +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + this->cleanup_info_.cleanup_hook_ = 0; + this->cleanup_info_.object_ = 0; + this->cleanup_info_.param_ = 0; +#else /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + this->at_exit_list_ = 0; + // Start the at_exit hook list. + this->tm_ = tm; + // Setup the Thread_Manager. + this->log_msg_ = 0; + this->terminated_ = 0; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ +} + +ACE_INLINE ACE_Thread_Descriptor * +ACE_Thread_Manager::thread_desc_self (void) +{ + // This method must be called with lock held. + + // Try to get it from cache. + ACE_Thread_Descriptor *desc = ACE_LOG_MSG->thr_desc (); + +#if 1 + ACE_ASSERT (desc != 0); + // Thread descriptor should always get cached. +#else + if (desc == 0) + { + ACE_thread_t id = ACE_OS::thr_self (); + + desc = this->find_thread (id); + + // Thread descriptor adapter might not have been put into the + // list yet. + if (desc != 0) + // Update the TSS cache. + ACE_LOG_MSG->thr_desc (desc); + } +#endif + return desc; +} + +// Return the unique ID of the thread. + +ACE_INLINE ACE_thread_t +ACE_Thread_Manager::thr_self (void) +{ + ACE_TRACE ("ACE_Thread_Manager::thr_self"); + return ACE_Thread::self (); +} + +ACE_INLINE ACE_Task_Base * +ACE_Thread_Manager::task (void) +{ + ACE_TRACE ("ACE_Thread_Manager::task"); + + ACE_Thread_Descriptor *td = this->thread_desc_self () ; + + if (td == 0) + return 0; + else + return td->task (); +} + +ACE_INLINE int +ACE_Thread_Manager::open (size_t) +{ + // Currently no-op. + return 0; +} + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) +ACE_INLINE int +ACE_Thread_Manager::at_exit (ACE_At_Thread_Exit* at) +{ + return this->thread_desc_self ()->at_exit (at); +} + +ACE_INLINE int +ACE_Thread_Manager::at_exit (ACE_At_Thread_Exit& at) +{ + return this->thread_desc_self ()->at_exit (at); +} +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +ACE_INLINE int +ACE_Thread_Manager::at_exit (void *object, + ACE_CLEANUP_FUNC cleanup_hook, + void *param) +{ + return this->thread_desc_self ()->at_exit (object, + cleanup_hook, + param); +} + +ACE_INLINE void +ACE_Thread_Manager::wait_on_exit (int do_wait) +{ + this->automatic_wait_ = do_wait; +} + +ACE_INLINE int +ACE_Thread_Manager::wait_on_exit (void) +{ + return this->automatic_wait_; +} + +ACE_INLINE int +ACE_Thread_Manager::register_as_terminated (ACE_Thread_Descriptor *td) +{ +#if defined (VXWORKS) + ACE_UNUSED_ARG (td); +#else /* ! VXWORKS */ + ACE_Thread_Descriptor_Base *tdb; + ACE_NEW_RETURN (tdb, ACE_Thread_Descriptor_Base (*td), -1); + this->terminated_thr_list_.insert_tail (tdb); +#endif /* ! VXWORKS */ + return 0; +} + +ACE_INLINE int +ACE_Thread_Manager::count_threads (void) const +{ + return this->thr_list_.size (); +} diff --git a/ace/Threads/Token.cpp b/ace/Threads/Token.cpp new file mode 100644 index 00000000000..cb2b2d2c618 --- /dev/null +++ b/ace/Threads/Token.cpp @@ -0,0 +1,545 @@ +// $Id$ + +#include "ace/Thread.h" +#include "ace/Token.h" +#include "ace/Log_Msg.h" + +#if defined (DEBUGGING) +#include "ace/streams.h" +#endif /* DEBUGGING */ + +ACE_RCSID(ace, Token, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#if !defined (__ACE_INLINE__) +#include "ace/Synch_T.h" +#include "ace/Token.i" +#endif /* __ACE_INLINE__ */ + +ACE_ALLOC_HOOK_DEFINE(ACE_Token) + + +void +ACE_Token::dump (void) const +{ + ACE_TRACE ("ACE_Token::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthread = %d"), ACE_Thread::self ())); + // @@ Is there a portable way to do this? + // ACE_DEBUG ((LM_DEBUG, "\nowner_ = %l", (long) this->owner_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nowner_ addr = %x"), &this->owner_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nwaiters_ = %d"), this->waiters_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nin_use_ = %d"), this->in_use_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nnesting level = %d"), this->nesting_level_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry (ACE_Thread_Mutex &m, + ACE_thread_t t_id) + : next_ (0), + thread_id_ (t_id), +#if defined (ACE_TOKEN_USES_SEMAPHORE) + cv_ (0), +#else + cv_ (m), +#endif /* ACE_TOKEN_USES_SEMAPHORE */ + runable_ (0) +{ +#if defined (ACE_TOKEN_USES_SEMAPHORE) + ACE_UNUSED_ARG (m); +#endif /* ACE_TOKEN_USES_SEMAPHORE */ + + ACE_TRACE ("ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry"); +} + +ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry (ACE_Thread_Mutex &m, + ACE_thread_t t_id, + ACE_Condition_Attributes &attributes) + : next_ (0), + thread_id_ (t_id), +#if defined (ACE_TOKEN_USES_SEMAPHORE) + cv_ (0), +#else + cv_ (m, attributes), +#endif /* ACE_TOKEN_USES_SEMAPHORE */ + runable_ (0) +{ +#if defined (ACE_TOKEN_USES_SEMAPHORE) + ACE_UNUSED_ARG (m); + ACE_UNUSED_ARG (attributes); +#endif /* ACE_TOKEN_USES_SEMAPHORE */ + + ACE_TRACE ("ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry"); +} + +ACE_Token::ACE_Token_Queue::ACE_Token_Queue (void) + : head_ (0), + tail_ (0) +{ + ACE_TRACE ("ACE_Token::ACE_Token_Queue::ACE_Token_Queue"); +} + +// +// Remove an entry from the list. Must be called with locks held. +// +void +ACE_Token::ACE_Token_Queue::remove_entry (ACE_Token::ACE_Token_Queue_Entry *entry) +{ + ACE_TRACE ("ACE_Token::ACE_Token_Queue::remove_entry"); + ACE_Token_Queue_Entry *curr = 0; + ACE_Token_Queue_Entry *prev = 0; + + if (this->head_ == 0) + return; + + for (curr = this->head_; + curr != 0 && curr != entry; + curr = curr->next_) + prev = curr; + + if (curr == 0) + // Didn't find the entry... + return; + else if (prev == 0) + // Delete at the head. + this->head_ = this->head_->next_; + else + // Delete in the middle. + prev->next_ = curr->next_; + + // We need to update the tail of the list if we've deleted the last + // entry. + if (curr->next_ == 0) + this->tail_ = prev; +} + +// +// Add an entry into the list. Must be called with locks held. +// +void +ACE_Token::ACE_Token_Queue::insert_entry (ACE_Token::ACE_Token_Queue_Entry &entry, + int requeue_position) +{ + if (this->head_ == 0) + { + // No other threads - just add me + this->head_ = &entry; + this->tail_ = &entry; + } + else if (requeue_position == -1) + { + // Insert at the end of the queue. + this->tail_->next_ = &entry; + this->tail_ = &entry; + } + else if (requeue_position == 0) + { + // Insert at head of queue. + entry.next_ = this->head_; + this->head_ = &entry; + } + else + // Insert in the middle of the queue somewhere. + { + // Determine where our thread should go in the queue of waiters. + + ACE_Token::ACE_Token_Queue_Entry *insert_after = this->head_; + while (requeue_position-- && insert_after->next_ != 0) + insert_after = insert_after->next_; + + entry.next_ = insert_after->next_; + + if (entry.next_ == 0) + this->tail_ = &entry; + + insert_after->next_ = &entry; + } +} + +ACE_Token::ACE_Token (const ACE_TCHAR *name, void *any) + : lock_ (name, (ACE_mutexattr_t *) any), + owner_ (ACE_OS::NULL_thread), + in_use_ (0), + waiters_ (0), + nesting_level_ (0), + attributes_ (USYNC_THREAD) +{ +// ACE_TRACE ("ACE_Token::ACE_Token"); +} + +ACE_Token::~ACE_Token (void) +{ + ACE_TRACE ("ACE_Token::~ACE_Token"); +} + +int +ACE_Token::shared_acquire (void (*sleep_hook_func)(void *), + void *arg, + ACE_Time_Value *timeout, + ACE_Token_Op_Type op_type) +{ + ACE_TRACE ("ACE_Token::shared_acquire"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + +#if defined (DEBUGGING) + this->dump (); +#endif /* DEBUGGING */ + + ACE_thread_t thr_id = ACE_Thread::self (); + + // Nobody holds the token. + if (!this->in_use_) + { + // Its mine! + this->in_use_ = op_type; + this->owner_ = thr_id; + return 0; + } + + // + // Someone already holds the token. + // + + // Check if it is us. + if (ACE_OS::thr_equal (thr_id, this->owner_)) + { + this->nesting_level_++; + return 0; + } + + // Do a quick check for "polling" behavior. + if (timeout != 0 && timeout->sec () == 0 && timeout->usec () == 0) + { + errno = ETIME; + return -1; + } + + // + // We've got to sleep until we get the token. + // + + // Which queue we should end up in... + ACE_Token_Queue *queue = (op_type == ACE_Token::READ_TOKEN + ? &this->readers_ + : &this->writers_); + + // Allocate queue entry on stack. This works since we don't exit + // this method's activation record until we've got the token. + ACE_Token::ACE_Token_Queue_Entry my_entry (this->lock_, + thr_id, + this->attributes_); + queue->insert_entry (my_entry); + this->waiters_++; + + // Execute appropriate callback. (@@ should these + // methods return a success/failure status, and if so, what should + // we do with it?) + int ret = 0; + if (sleep_hook_func) + { + (*sleep_hook_func) (arg); + ret++; + } + else + { + // Execute virtual method. + this->sleep_hook (); + ret++; + } + + int timed_out = 0; + int error = 0; + + // Sleep until we've got the token (ignore signals). + do + { + int result = my_entry.wait (timeout, + this->lock_); + + if (result == -1) + { + // Note, this should obey whatever thread-specific interrupt + // policy is currently in place... + if (errno == EINTR) + continue; + +#if defined (DEBUGGING) + cerr << '(' << ACE_Thread::self () << ')' + << " acquire: " + << (errno == ETIME ? "timed out" : "error occurred") + << endl; +#endif /* DEBUGGING */ + + // We come here if a timeout occurs or some serious + // ACE_Condition object error. + if (errno == ETIME) + timed_out = 1; + else + error = 1; + + // Stop the loop. + break; + } + } + while (!ACE_OS::thr_equal (thr_id, this->owner_)); + + // Do this always and irrespective of the result of wait(). + this->waiters_--; + queue->remove_entry (&my_entry); + +#if defined (DEBUGGING) + cerr << '(' << ACE_Thread::self () << ')' + << " acquire (UNBLOCKED)" << endl; +#endif /* DEBUGGING */ + + // If timeout occured + if (timed_out) + { + // This thread was still selected to own the token. + if (my_entry.runable_) + { + // Wakeup next waiter since this thread timed out. + this->wakeup_next_waiter (); + } + + // Return error. + return -1; + } + else if (error) + { + // Return error. + return -1; + } + + // If this is a normal wakeup, this thread should be runnable. + ACE_ASSERT (my_entry.runable_); + + return ret; +} + +// By default this is a no-op. + +/* virtual */ +void +ACE_Token::sleep_hook (void) +{ + ACE_TRACE ("ACE_Token::sleep_hook"); +} + +int +ACE_Token::acquire (ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Token::acquire"); + return this->shared_acquire (0, 0, timeout, ACE_Token::WRITE_TOKEN); +} + +// Acquire the token, sleeping until it is obtained or until +// expires. + +int +ACE_Token::acquire (void (*sleep_hook_func)(void *), + void *arg, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Token::acquire"); + return this->shared_acquire (sleep_hook_func, arg, timeout, ACE_Token::WRITE_TOKEN); +} + +// Try to renew the token. + +int +ACE_Token::renew (int requeue_position, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Token::renew"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + +#if defined (DEBUGGING) + this->dump (); +#endif /* DEBUGGING */ + // ACE_ASSERT (ACE_OS::thr_equal (ACE_Thread::self (), this->owner_)); + + // Check to see if there are any waiters worth giving up the lock + // for. + + // If no writers and either we are a writer or there are no readers. + if (this->writers_.head_ == 0 && + (this->in_use_ == ACE_Token::WRITE_TOKEN || + this->readers_.head_ == 0)) + // Immediate return. + return 0; + + // We've got to sleep until we get the token again. + + // Determine which queue should this thread go to. + ACE_Token::ACE_Token_Queue *this_threads_queue = + this->in_use_ == ACE_Token::READ_TOKEN ? + &this->readers_ : &this->writers_; + + ACE_Token::ACE_Token_Queue_Entry my_entry (this->lock_, + this->owner_); + + this_threads_queue->insert_entry (my_entry, + requeue_position); + this->waiters_++; + + // Remember nesting level... + int save_nesting_level_ = this->nesting_level_; + + // Reset state for new owner. + this->nesting_level_ = 0; + + // Wakeup waiter. + this->wakeup_next_waiter (); + + int timed_out = 0; + int error = 0; + + // Sleep until we've got the token (ignore signals). + do + { + int result = my_entry.wait (timeout, + this->lock_); + + if (result == -1) + { + // Note, this should obey whatever thread-specific interrupt + // policy is currently in place... + if (errno == EINTR) + continue; + +#if defined (DEBUGGING) + cerr << '(' << ACE_Thread::self () << ')' + << " renew: " + << (errno == ETIME ? "timed out" : "error occurred") + << endl; +#endif /* DEBUGGING */ + + // We come here if a timeout occurs or some serious + // ACE_Condition object error. + if (errno == ETIME) + timed_out = 1; + else + error = 1; + + // Stop the loop. + break; + } + } + while (!ACE_OS::thr_equal (my_entry.thread_id_, this->owner_)); + + // Do this always and irrespective of the result of wait(). + this->waiters_--; + this_threads_queue->remove_entry (&my_entry); + +#if defined (DEBUGGING) + cerr << '(' << ACE_Thread::self () << ')' + << " acquire (UNBLOCKED)" << endl; +#endif /* DEBUGGING */ + + // If timeout occured + if (timed_out) + { + // This thread was still selected to own the token. + if (my_entry.runable_) + { + // Wakeup next waiter since this thread timed out. + this->wakeup_next_waiter (); + } + + // Return error. + return -1; + } + else if (error) + { + // Return error. + return -1; + } + + // If this is a normal wakeup, this thread should be runnable. + ACE_ASSERT (my_entry.runable_); + + // Reinstate nesting level. + this->nesting_level_ = save_nesting_level_; + + return 0; +} + +// Release the current holder of the token (which had +// better be the caller's thread!). + +int +ACE_Token::release (void) +{ + ACE_TRACE ("ACE_Token::release"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + + // ACE_ASSERT (ACE_OS::thr_equal (ACE_Thread::self (), this->owner_)); + +#if defined (DEBUGGING) + this->dump (); +#endif /* DEBUGGING */ + + // Nested release... + if (this->nesting_level_ > 0) + --this->nesting_level_; + else + { + // + // Regular release... + // + + // Wakeup waiter. + this->wakeup_next_waiter (); + } + + return 0; +} + +void +ACE_Token::wakeup_next_waiter (void) +{ + ACE_TRACE ("ACE_Token::wakeup_next_waiter"); + + // Reset state for new owner. + this->owner_ = ACE_OS::NULL_thread; + this->in_use_ = 0; + + // Any waiters... + if (this->writers_.head_ == 0 && + this->readers_.head_ == 0) + { + // No more waiters... + return; + } + + // Wakeup next waiter. + ACE_Token_Queue *queue; + + // Writer threads get priority to run first. + if (this->writers_.head_ != 0) + { + this->in_use_ = ACE_Token::WRITE_TOKEN; + queue = &this->writers_; + } + else + { + this->in_use_ = ACE_Token::READ_TOKEN; + queue = &this->readers_; + } + + // Wake up waiter and make it runable. + queue->head_->runable_ = 1; + queue->head_->signal (); + + this->owner_ = queue->head_->thread_id_; +} + +#endif /* ACE_HAS_THREADS */ + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Threads/Token.h b/ace/Threads/Token.h new file mode 100644 index 00000000000..8d9cc641059 --- /dev/null +++ b/ace/Threads/Token.h @@ -0,0 +1,290 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Token.h + * + * $Id$ + * + * @author Original author + * @author Karl-Heinz Dorn (kdorn@erlh.siemens.de) + * @author Ported to ACE by + * @author Douglas C. Schmidt (schmidt@cs.wustl.edu) + */ +//============================================================================= + +#ifndef ACE_TOKEN_H +#define ACE_TOKEN_H +#include "ace/pre.h" + +#include "ace/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_HAS_THREADS) + +#if (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || defined (VXWORKS) || defined (ACE_PSOS) +// If platforms support semaphores with timed wait, then we use semaphores instead of c.v. +# define ACE_TOKEN_USES_SEMAPHORE +#endif /* (ACE_WIN32 && !ACE_HAS_WINCE) || VXWORKS || ACE_PSOS */ + +/** + * @class ACE_Token + * + * @brief Class that acquires, renews, and releases a synchronization + * token that is serviced in strict FIFO ordering and that also + * supports (1) recursion and (2) readers/writer semantics. + * + * This class is a more general-purpose synchronization mechanism + * than many native OS mutexes. For example, it implements + * "recursive mutex" semantics, where a thread that owns the token + * can reacquire it without deadlocking. If the same thread calls + * multiple times, however, it must call an + * equal number of times before the token is actually released. + * Threads that are blocked awaiting the token are serviced in + * strict FIFO order as other threads release the token (Solaris + * and Pthread mutexes don't strictly enforce an acquisition + * order). There are two FIFO lists within the class. Write + * acquires always have higher priority over read acquires. Which + * means, if you use both write/read operations, care must be + * taken to avoid starvation on the readers. Notice that the + * read/write acquire operations do not have the usual semantic of + * reader/writer locks. Only one reader can acquire the token at + * a time (which is different from the usual reader/writer locks + * where several readers can acquire a lock at the same time as + * long as there is no writer waiting for the lock). We choose + * the names to (1) borrow the semantic to give writers higher + * priority and (2) support a common interface for all locking + * classes in ACE. + */ +class ACE_Export ACE_Token +{ +public: + // = Initialization and termination. + + ACE_Token (const ACE_TCHAR *name = 0, void * = 0); + virtual ~ACE_Token (void); + + // = Synchronization operations. + + /** + * Acquire the token, sleeping until it is obtained or until the + * expiration of , which is treated as "absolute" time. If + * some other thread currently holds the token then is + * called before our thread goes to sleep. This can be + * used by the requesting thread to unblock a token-holder that is + * sleeping, e.g., by means of writing to a pipe (the ACE + * ACE_Reactor uses this functionality). Return values: 0 if + * acquires without calling 1 if is + * called. 2 if the token is signaled. -1 if failure or timeout + * occurs (if timeout occurs errno == ETIME) If == + * <&ACE_Time_Value::zero> then acquire has polling semantics (and + * does *not* call ). + */ + int acquire (void (*sleep_hook)(void *), + void *arg = 0, + ACE_Time_Value *timeout = 0); + + /** + * This behaves just like the previous method, except that + * it invokes the virtual function called that can be + * overridden by a subclass of ACE_Token. + */ + int acquire (ACE_Time_Value *timeout = 0); + + /** + * This should be overridden by a subclass to define the appropriate + * behavior before goes to sleep. By default, this is a + * no-op... + */ + virtual void sleep_hook (void); + + /** + * An optimized method that efficiently reacquires the token if no + * other threads are waiting. This is useful for situations where + * you don't want to degrade the quality of service if there are + * other threads waiting to get the token. If == + * -1 and there are other threads waiting to obtain the token we are + * queued at the end of the list of waiters. If + * > -1 then it indicates how many entries to skip over before + * inserting our thread into the list of waiters (e.g., + * == 0 means "insert at front of the queue"). + * Renew has the rather odd semantics such that if there are other + * waiting threads it will give up the token even if the + * nesting_level_ > 1. I'm not sure if this is really the right + * thing to do (since it makes it possible for shared data to be + * changed unexpectedly) so use with caution... This method + * maintians the original token priority. As in , the + * value is an absolute time. + */ + int renew (int requeue_position = 0, + ACE_Time_Value *timeout = 0); + + /// Become interface-compliant with other lock mechanisms (implements + /// a non-blocking ). + int tryacquire (void); + + /// Shuts down the ACE_Token instance. + int remove (void); + + /// Relinquish the token. If there are any waiters then the next one + /// in line gets it. + int release (void); + + /// Behave like acquire but in a lower priority. It should probably + /// be called acquire_yield. + int acquire_read (void); + + /// More sophisticate version of acquire_read. + int acquire_read (void (*sleep_hook)(void *), + void *arg = 0, + ACE_Time_Value *timeout = 0); + + /// Just calls . + int acquire_write (void); + + /// More sophisticate version of acquire_write. + int acquire_write (void (*sleep_hook)(void *), + void *arg = 0, + ACE_Time_Value *timeout = 0); + + /// Lower priority try_acquire. + int tryacquire_read (void); + + /// Just calls . + int tryacquire_write (void); + + /// Assumes the caller has acquired the token and returns 0. + int tryacquire_write_upgrade (void); + + // = Accessor methods. + + /// Return the number of threads that are currently waiting to get + /// the token. + int waiters (void); + + /// Return the id of the current thread that owns the token. + ACE_thread_t current_owner (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + // = The following structure implements a ACE_FIFO of waiter threads + // that are asleep waiting to obtain the token. + + struct ACE_Token_Queue_Entry + { + ACE_Token_Queue_Entry (ACE_Thread_Mutex &m, + ACE_thread_t t_id); + // Constructor + + ACE_Token_Queue_Entry (ACE_Thread_Mutex &m, + ACE_thread_t t_id, + ACE_Condition_Attributes &attributes); + // Constructor using a pre-allocated attributes + + int wait (ACE_Time_Value *timeout, ACE_Thread_Mutex &lock); + // Entry blocks on the token. + + int signal (void); + // Notify (unblock) the entry. + + ACE_Token_Queue_Entry *next_; + // Pointer to next waiter. + + ACE_thread_t thread_id_; + // ACE_Thread id of this waiter. + +#if defined (ACE_TOKEN_USES_SEMAPHORE) + ACE_Semaphore cv_; + // ACE_Semaphore object used to wake up waiter when it can run again. +#else + ACE_Condition_Thread_Mutex cv_; + // ACE_Condition object used to wake up waiter when it can run again. +#endif /* ACE_TOKEN_USES_SEMAPHORE */ + + int runable_; + // Ok to run. + }; + +private: + enum ACE_Token_Op_Type + { + READ_TOKEN = 1, + WRITE_TOKEN + }; + + struct ACE_Token_Queue + { + ACE_Token_Queue (void); + + void remove_entry (ACE_Token_Queue_Entry *); + // Remove a waiter from the queue. + + void insert_entry (ACE_Token_Queue_Entry &entry, + int requeue_position = -1); + // Insert a waiter into the queue. + + ACE_Token_Queue_Entry *head_; + // Head of the list of waiting threads. + + ACE_Token_Queue_Entry *tail_; + // Tail of the list of waiting threads. + }; + + /// Implements the and methods above. + int shared_acquire (void (*sleep_hook_func)(void *), + void *arg, + ACE_Time_Value *timeout, + ACE_Token_Op_Type op_type); + + /// Wake next in line for ownership. + void wakeup_next_waiter (void); + + /// A queue of writer threads. + ACE_Token_Queue writers_; + + /// A queue of reader threads. + ACE_Token_Queue readers_; + + /// ACE_Thread_Mutex used to lock internal data structures. + ACE_Thread_Mutex lock_; + + /// Current owner of the token. + ACE_thread_t owner_; + + /// Some thread (i.e., ) is using the token. We need this + /// extra variable to deal with POSIX pthreads madness... + int in_use_; + + /// Number of waiters. + int waiters_; + + /// Current nesting level. + int nesting_level_; + + /// The attributes for the condition variables, optimizes lock time. + ACE_Condition_Attributes attributes_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Synch_T.h" +#include "ace/Token.i" +#endif /* __ACE_INLINE__ */ +#else +class ACE_Export ACE_Token +{ +public: + int acquire (ACE_Time_Value * = 0) { ACE_NOTSUP_RETURN (-1); } + int tryacquire (void) { ACE_NOTSUP_RETURN (-1); } + int remove (void) { ACE_NOTSUP_RETURN (-1); } + int release (void) { ACE_NOTSUP_RETURN (-1); } +}; +#endif /* ACE_HAS_THREADS */ +#include "ace/post.h" +#endif /* ACE_TOKEN_H */ diff --git a/ace/Threads/Token.i b/ace/Threads/Token.i new file mode 100644 index 00000000000..e5b7aaf94a9 --- /dev/null +++ b/ace/Threads/Token.i @@ -0,0 +1,123 @@ +/* -*- C++ -*- */ +// $Id$ + +// Token.i + +ACE_INLINE int +ACE_Token::remove (void) +{ + ACE_TRACE ("ACE_Token::remove"); + // Don't have an implementation for this yet... + ACE_NOTSUP_RETURN (-1); +} + +ACE_INLINE int +ACE_Token::tryacquire (void) +{ + ACE_TRACE ("ACE_Token::tryacquire"); + return this->shared_acquire + (0, 0, (ACE_Time_Value *) &ACE_Time_Value::zero, ACE_Token::WRITE_TOKEN); +} + +ACE_INLINE int +ACE_Token::waiters (void) +{ + ACE_TRACE ("ACE_Token::waiters"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + + int ret = this->waiters_; + return ret; +} + +ACE_INLINE ACE_thread_t +ACE_Token::current_owner (void) +{ + ACE_TRACE ("ACE_Token::current_owner"); + ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, this->owner_); + + return this->owner_; +} + +ACE_INLINE int +ACE_Token::acquire_read (void) +{ + ACE_TRACE ("ACE_Token::acquire_read"); + return this->shared_acquire + (0, 0, 0, ACE_Token::READ_TOKEN); +} + +ACE_INLINE int +ACE_Token::acquire_write (void) +{ + ACE_TRACE ("ACE_Token::acquire_write"); + return this->shared_acquire + (0, 0, 0, ACE_Token::WRITE_TOKEN); +} + +ACE_INLINE int +ACE_Token::tryacquire_read (void) +{ + ACE_TRACE ("ACE_Token::tryacquire_read"); + return this->shared_acquire + (0, 0, (ACE_Time_Value *) &ACE_Time_Value::zero, ACE_Token::READ_TOKEN); +} + +ACE_INLINE int +ACE_Token::acquire_read (void (*sleep_hook_func)(void *), + void *arg, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Token::acquire_read"); + return this->shared_acquire (sleep_hook_func, arg, timeout, ACE_Token::READ_TOKEN); +} + +ACE_INLINE int +ACE_Token::tryacquire_write (void) +{ + ACE_TRACE ("ACE_Token::tryacquire_write"); + return this->shared_acquire + (0, 0, (ACE_Time_Value *) &ACE_Time_Value::zero, ACE_Token::WRITE_TOKEN); +} + +ACE_INLINE int +ACE_Token::tryacquire_write_upgrade (void) +{ + ACE_TRACE ("ACE_Token::tryacquire_write_upgrade"); + return 0; +} + +ACE_INLINE int +ACE_Token::acquire_write (void (*sleep_hook_func)(void *), + void *arg, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Token::acquire_write"); + return this->shared_acquire (sleep_hook_func, arg, timeout, ACE_Token::WRITE_TOKEN); +} + +ACE_INLINE int +ACE_Token::ACE_Token_Queue_Entry::wait (ACE_Time_Value *timeout, ACE_Thread_Mutex &lock) +{ +#if defined (ACE_TOKEN_USES_SEMAPHORE) + lock.release (); + int retv = (timeout == 0 ? + this->cv_.acquire () : + this->cv_.acquire (*timeout)); + lock.acquire (); + return retv; +#else + ACE_UNUSED_ARG (lock); + return this->cv_.wait (timeout); +#endif /* ACE_TOKEN_USES_SEMAPHORE */ +} + +ACE_INLINE int +ACE_Token::ACE_Token_Queue_Entry::signal (void) +{ + return +#if defined (ACE_TOKEN_USES_SEMAPHORE) + this->cv_.release (); +#else + this->cv_.signal (); +#endif /* ACE_TOKEN_USES_SEMAPHORE */ +} diff --git a/ace/Timer/Basic_Stats.cpp b/ace/Timer/Basic_Stats.cpp new file mode 100644 index 00000000000..00509d827ce --- /dev/null +++ b/ace/Timer/Basic_Stats.cpp @@ -0,0 +1,73 @@ +// $Id$ + +#include "ace/Basic_Stats.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Basic_Stats.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Basic_Stats, "$Id$") + +void +ACE_Basic_Stats::accumulate (const ACE_Basic_Stats &rhs) +{ + if (rhs.samples_count_ == 0) + return; + + if (this->samples_count_ == 0) + { + this->samples_count_ = rhs.samples_count_; + + this->min_ = rhs.min_; + this->max_ = rhs.max_; + this->sum_ = rhs.sum_; + this->sum2_ = rhs.sum2_; + + return; + } + this->samples_count_ += rhs.samples_count_; + + if (this->min_ > rhs.min_) + this->min_ = rhs.min_; + if (this->max_ < rhs.max_) + this->max_ = rhs.max_; + + this->sum_ += rhs.sum_; + this->sum2_ += rhs.sum2_; +} + +void +ACE_Basic_Stats::dump_results (const ACE_TCHAR *msg, + ACE_UINT32 sf) const +{ + if (this->samples_count () == 0u) + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("%s : no data collected\n"), msg)); + return; + } + + ACE_UINT64 avg = this->sum_ / this->samples_count_; + ACE_UINT64 dev = +#if defined ACE_LACKS_LONGLONG_T + ACE_static_cast (ACE_U_LongLong, + this->sum2_ / this->samples_count_) + - avg * ACE_U64_TO_U32(avg); +#else /* ! ACE_LACKS_LONGLONG_T */ + this->sum2_ / this->samples_count_ - avg * avg; +#endif /* ! ACE_LACKS_LONGLONG_T */ + + double l_min = ACE_CU64_TO_CU32 (this->min_) / sf; + double l_max = ACE_CU64_TO_CU32 (this->max_) / sf; + double l_avg = ACE_CU64_TO_CU32 (avg) / sf; + double l_dev = ACE_CU64_TO_CU32 (dev) / (sf * sf); + + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("%s latency : %.2f[%d]/%.2f/%.2f[%d]/%.2f (min/avg/max/var^2)\n"), + msg, + l_min, this->min_at_, + l_avg, + l_max, this->max_at_, + l_dev)); +} diff --git a/ace/Timer/Basic_Stats.h b/ace/Timer/Basic_Stats.h new file mode 100644 index 00000000000..722c92ae355 --- /dev/null +++ b/ace/Timer/Basic_Stats.h @@ -0,0 +1,87 @@ + +//============================================================================= +/** + * @file Basic_Stats.h + * + * $Id$ + * + * @author Carlos O'Ryan + */ +//============================================================================= + + +#ifndef ACE_BASIC_STATS_H +#define ACE_BASIC_STATS_H +#include "ace/pre.h" + +#include "ace/config-all.h" +#include "ace/Basic_Types.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/// Collect basic stats about a series of samples +/** + * Compute the average and standard deviation (aka jitter) for an + * arbitrary number of samples, using constant space. + * Normally used for latency statistics. + */ +class ACE_Export ACE_Basic_Stats +{ +public: + /// Constructor + /** + * The number of samples is pre-allocated, and cannot changes once + * the class is initialized. + */ + ACE_Basic_Stats (void); + + /// The number of samples received so far + ACE_UINT32 samples_count (void) const; + + /// Record one sample. + void sample (ACE_UINT64 value); + + /// Update the values to reflect the stats in @param rhs + void accumulate (const ACE_Basic_Stats &rhs); + + /// Dump all the samples + /** + * Prints out the results, using @param msg as a prefix for each + * message and scaling all the numbers by @param scale_factor. + * The latter is useful because high resolution timer samples are + * acquired in clock ticks, but often presented in microseconds. + */ + void dump_results (const ACE_TCHAR *msg, + ACE_UINT32 scale_factor) const; + +private: + /// The number of samples + ACE_UINT32 samples_count_; + + /// The minimum value + ACE_UINT64 min_; + + /// The number of the sample that had the minimum value + ACE_UINT32 min_at_; + + /// The maximum value + ACE_UINT64 max_; + + /// The number of the sample that had the maximum value + ACE_UINT32 max_at_; + + /// The sum of all the values + ACE_UINT64 sum_; + + /// The sum of the square of all the values + ACE_UINT64 sum2_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Basic_Stats.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_BASIC_STATS_H */ diff --git a/ace/Timer/Basic_Stats.inl b/ace/Timer/Basic_Stats.inl new file mode 100644 index 00000000000..4ed1baca459 --- /dev/null +++ b/ace/Timer/Basic_Stats.inl @@ -0,0 +1,59 @@ +// $Id$ + +ACE_INLINE +ACE_Basic_Stats::ACE_Basic_Stats (void) + : samples_count_ (0) + , min_ (0) + , min_at_ (0) + , max_ (0) + , max_at_ (0) + , sum_ (0) + , sum2_ (0) +{ +} + +ACE_INLINE ACE_UINT32 +ACE_Basic_Stats::samples_count (void) const +{ + return this->samples_count_; +} + +ACE_INLINE void +ACE_Basic_Stats::sample (ACE_UINT64 value) +{ + ++this->samples_count_; + + if (this->samples_count_ == 1u) + { + this->min_ = value; + this->min_at_ = this->samples_count_; + this->max_ = value; + this->max_at_ = this->samples_count_; + this->sum_ = value; +#if defined ACE_LACKS_LONGLONG_T + this->sum2_ = value * ACE_U64_TO_U32 (value); +#else /* ! ACE_LACKS_LONGLONG_T */ + this->sum2_ = value * value; +#endif /* ! ACE_LACKS_LONGLONG_T */ + } + else + { + if (this->min_ > value) + { + this->min_ = value; + this->min_at_ = this->samples_count_; + } + if (this->max_ < value) + { + this->max_ = value; + this->max_at_ = this->samples_count_; + } + + this->sum_ += value; +#if defined ACE_LACKS_LONGLONG_T + this->sum2_ += value * ACE_U64_TO_U32 (value); +#else /* ! ACE_LACKS_LONGLONG_T */ + this->sum2_ += value * value; +#endif /* ! ACE_LACKS_LONGLONG_T */ + } +} diff --git a/ace/Timer/High_Res_Timer.cpp b/ace/Timer/High_Res_Timer.cpp new file mode 100644 index 00000000000..c589ab67abc --- /dev/null +++ b/ace/Timer/High_Res_Timer.cpp @@ -0,0 +1,525 @@ +// $Id$ + +// Be very carefull before changing the calculations inside +// ACE_High_Res_Timer. The precision matters and we are using integer +// calculations not floating point. Also look good at the emulated 64 +// bit int class (inside Basic_Types{h,i,cpp} before changing +// anything. It's operator/ only returns 32 bits not 64 bits, among +// other things. + +#include "ace/High_Res_Timer.h" + +#if !defined (__ACE_INLINE__) +#include "ace/High_Res_Timer.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/Stats.h" + +ACE_RCSID(ace, High_Res_Timer, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_High_Res_Timer) + +// For Intel platforms, a scale factor is required for +// ACE_OS::gethrtime. We'll still set this to one to prevent division +// by zero errors. +#if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \ + defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \ + !defined (ACE_HAS_HI_RES_TIMER) + +# include "ace/Synch.h" +# include "ace/Object_Manager.h" + + // Initialize the global_scale_factor_ to 1. The first + // ACE_High_Res_Timer instance construction will override this + // value. + /* static */ + ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1u; + +#else /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \ + ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) || + ACE_HAS_HI_RES_TIMER */ + // A scale_factor of 1000 converts nanosecond ticks to microseconds. + // That is, on these platforms, 1 tick == 1 nanosecond. + /* static */ + ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1000u; +#endif /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \ + ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) || + ACE_HAS_HI_RES_TIMER */ + +// This is used to tell if the global_scale_factor_ has been +// set, and if high resolution timers are supported. +/* static */ +int ACE_High_Res_Timer::global_scale_factor_status_ = 0; + + +#if defined (linux) +// Determine the apparent CPU clock speed from /proc/cpuinfo +ACE_UINT32 +ACE_High_Res_Timer::get_cpuinfo (void) +{ + ACE_UINT32 scale_factor = 1u; + + // Get the BogoMIPS from /proc/cpuinfo. It works fine on Alpha and + // Pentium Pro. For other CPUs, it will be necessary to interpret + // the BogoMips, as described in the BogoMips mini-HOWTO. Note that + // this code assumes an order to the /proc/cpuinfo contents. The + // BogoMips rating had better come after CPU type and model info. +#if !defined (__alpha__) + int supported = 0; +#endif /* __alpha__ */ + + FILE *cpuinfo = ACE_OS::fopen ("/proc/cpuinfo", "r"); + + if (cpuinfo != 0) + { + ACE_TCHAR buf[128]; + + // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nReading /proc/cpuinfo..."))); + + while (ACE_OS::fgets (buf, sizeof buf, cpuinfo)) + { +#if defined (__alpha__) + ACE_UINT32 whole; + ACE_UINT32 fractional; + if (::sscanf (buf, + "BogoMIPS : %d.%d\n", + &whole, + &fractional) == 2 + || ::sscanf (buf, + "bogomips : %d.%d\n", + &whole, + &fractional) == 2) + { + scale_factor = whole; + break; + } +#else + double mhertz = 1; + double bmips = 1; + ACE_TCHAR arg[128]; + + // CPU type? + if (::sscanf (buf, "cpu : %s\n", arg) == 1) + { + // If this is an Alpha chip, then the BogoMips rating is + // usable... + if (ACE_OS::strncmp (arg, + "Alpha", + 5) == 0) + { + supported = 1; + // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" recognized Alpha chip..."))); + } + } + // Pentium CPU model? + else if (supported == 0 + && ::sscanf (buf, "model name : Pentium %s\n", arg) == 1) + { + // But if we don't have the right kind of Intel chip, + // just quit. + if (ACE_OS::strcmp (arg, "II") == 0 + || ACE_OS::strcmp (arg, "III") == 0 + || ACE_OS::strcmp (arg, "IV") == 0 + || ACE_OS::strcmp (arg, "Pro") == 0) + { + supported = 1; + // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" recognized Pentium Pro/II chip..."))); + } + } + else if (::sscanf (buf, "cpu MHz : %lf\n", &mhertz) == 1) + { + if (supported) + { + scale_factor = (ACE_UINT32) (mhertz + 0.5); + } + break; + } + else if (::sscanf (buf, "bogomips : %lf\n", &bmips) == 1 + || ::sscanf (buf, "BogoMIPS : %lf\n", &bmips) == 1) + { + if (supported) + { + scale_factor = (ACE_UINT32) (bmips + 0.5); + // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" setting the clock scale factor to %u"), scale_factor)); + } +#if 0 + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("\nThe BogoMIPS metric is not supported on this platform" + "\n\tReport the results of the clock calibration and" + "\n\tthe contents of /proc/cpuinfo to the ace-users mailing list"))); + } +#endif /* 0 */ + break; + } +#endif /* __alpha__ */ + } + + // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (done)\n"))); + + ACE_OS::fclose (cpuinfo); + } + + return scale_factor; +} +#endif /* linux */ + +ACE_UINT32 +ACE_High_Res_Timer::global_scale_factor (void) +{ +#if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \ + defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \ + !defined (ACE_HAS_HI_RES_TIMER) && \ + ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || \ + defined (ghs) || defined (__GNUG__) || defined (__KCC)) + // Check if the global scale factor needs to be set, and do if so. + if (ACE_High_Res_Timer::global_scale_factor_status_ == 0) + { + // Grab ACE's static object lock. This doesn't have anything to + // do with static objects; it's just a convenient lock to use. + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + // Double check + if (ACE_High_Res_Timer::global_scale_factor_status_ == 0) + { +# if defined (ACE_WIN32) + LARGE_INTEGER freq; + if (::QueryPerformanceFrequency (&freq)) + { + // We have a high-res timer +# if defined (ACE_LACKS_LONGLONG_T) + ACE_UINT64 uint64_freq(freq.u.LowPart, (ACE_UINT32) freq.u.HighPart); + ACE_High_Res_Timer::global_scale_factor + (uint64_freq / (ACE_UINT32) ACE_ONE_SECOND_IN_USECS); +# else + ACE_High_Res_Timer::global_scale_factor + (ACE_static_cast (unsigned int, + freq.QuadPart / ACE_HR_SCALE_CONVERSION)); +# endif // (ACE_LACKS_LONGLONG_T) + + ACE_High_Res_Timer::global_scale_factor_status_ = 1; + } + else + // High-Res timers not supported + ACE_High_Res_Timer::global_scale_factor_status_ = -1; + + return ACE_High_Res_Timer::global_scale_factor_; + +# elif defined (linux) + ACE_High_Res_Timer::global_scale_factor (ACE_High_Res_Timer::get_cpuinfo ()); +# endif /* ! ACE_WIN32 && ! (linux && __alpha__) */ + +# if !defined (ACE_WIN32) + if (ACE_High_Res_Timer::global_scale_factor_ == 1u) + // Failed to retrieve CPU speed from system, so calculate it. + ACE_High_Res_Timer::calibrate (); +# endif // (ACE_WIN32) + } + } + + ACE_High_Res_Timer::global_scale_factor_status_ = 1; +#endif /* (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \ + ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) && \ + ! ACE_HAS_HIGH_RES_TIMER && + ((WIN32 && ! WINCE) || ghs || __GNUG__) */ + + return ACE_High_Res_Timer::global_scale_factor_; +} + +ACE_High_Res_Timer::ACE_High_Res_Timer (void) +{ + ACE_TRACE ("ACE_High_Res_Timer::ACE_High_Res_Timer"); + + this->reset (); + + // Make sure that the global scale factor is set. + (void) global_scale_factor (); +} + +ACE_UINT32 +ACE_High_Res_Timer::calibrate (const ACE_UINT32 usec, + const u_int iterations) +{ + const ACE_Time_Value sleep_time (0, usec); + ACE_Stats delta_hrtime; + // In units of 100 usec, to avoid overflow. + ACE_Stats actual_sleeps; + + for (u_int i = 0; + i < iterations; + ++i) + { + const ACE_Time_Value actual_start = + ACE_OS::gettimeofday (); + const ACE_hrtime_t start = + ACE_OS::gethrtime (); + ACE_OS::sleep (sleep_time); + const ACE_hrtime_t stop = + ACE_OS::gethrtime (); + const ACE_Time_Value actual_delta = + ACE_OS::gettimeofday () - actual_start; + + // Store the sample. + delta_hrtime.sample (ACE_U64_TO_U32 (stop - start)); + actual_sleeps.sample (actual_delta.msec () * 100u); + } + + // Calculate the mean value of the samples, with no fractional + // precision. Use it for the global scale factor. + ACE_Stats_Value ticks (0); + delta_hrtime.mean (ticks); + + ACE_Stats_Value actual_sleep (0); + actual_sleeps.mean (actual_sleep); + + // The addition of 5 below rounds instead of truncates. + const ACE_UINT32 scale_factor = + (ticks.whole () / actual_sleep.whole () + 5) / + 10u /* usec/100 usec */; + ACE_High_Res_Timer::global_scale_factor (scale_factor); + + return scale_factor; +} + +void +ACE_High_Res_Timer::dump (void) const +{ + ACE_TRACE ("ACE_High_Res_Timer::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nglobal_scale_factor_: %u\n"), + global_scale_factor ())); +#if defined (ACE_LACKS_LONGLONG_T) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT (":\nstart_.hi (): %8x; start_.lo (): %8x;\n") + ACE_LIB_TEXT ("end_.hi (): %8x; end_.lo (): %8x;\n") + ACE_LIB_TEXT ("total_.hi (): %8x; total_.lo (): %8x;\n") + ACE_LIB_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"), + start_.hi (), start_.lo (), + end_.hi (), end_.lo (), + total_.hi (), total_.lo (), + start_incr_.hi (), start_incr_.lo ())); +#else /* ! ACE_LACKS_LONGLONG_T */ + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT (":\nstart_.hi (): %8x; start_.lo (): %8x;\n") + ACE_LIB_TEXT ("end_.hi (): %8x; end_.lo (): %8x;\n") + ACE_LIB_TEXT ("total_.hi (): %8x; total_.lo (): %8x;\n") + ACE_LIB_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"), + ACE_CU64_TO_CU32 (start_ >> 32), + ACE_CU64_TO_CU32 (start_ & 0xfffffffful), + ACE_CU64_TO_CU32 (end_ >> 32), + ACE_CU64_TO_CU32 (end_ & 0xfffffffful), + ACE_CU64_TO_CU32 (total_ >> 32), + ACE_CU64_TO_CU32 (total_ & 0xfffffffful), + ACE_CU64_TO_CU32 (start_incr_ >> 32), + ACE_CU64_TO_CU32 (start_incr_ & 0xfffffffful))); +#endif /* ! ACE_LACKS_LONGLONG_T */ + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +void +ACE_High_Res_Timer::reset (void) +{ + ACE_TRACE ("ACE_High_Res_Timer::reset"); + + start_ = 0; + end_ = 0; + total_ = 0; + start_incr_ = 0; +} + +void +ACE_High_Res_Timer::elapsed_time (ACE_Time_Value &tv) const +{ + hrtime_to_tv (tv, end_ - start_); +} + +#if defined (ACE_HAS_POSIX_TIME) +// Note... Win32 does not have ACE_HAS_POSIX_TIME, so the scale factor +// does not need to take into account the different units on Win32. + +void +ACE_High_Res_Timer::elapsed_time (struct timespec &elapsed_time) const +{ + // This implementation should be cleaned up. + + // Just grab the nanoseconds. That is, leave off all values above + // microsecond. This equation is right! Don't mess with me! (It + // first strips off everything but the portion less than 1 usec. + // Then it converts that to nanoseconds by dividing by the scale + // factor to convert to usec, and multiplying by 1000.) The cast + // avoids a MSVC 4.1 compiler warning about narrowing. + u_long nseconds = ACE_static_cast (u_long, + (this->end_ - this->start_) % + global_scale_factor () * 1000u / + global_scale_factor ()); + + // Get just the microseconds (dropping any left over nanoseconds). + ACE_UINT32 useconds = (ACE_UINT32) ((this->end_ - this->start_) / global_scale_factor ()); + +#if ! defined(ACE_HAS_BROKEN_TIMESPEC_MEMBERS) + elapsed_time.tv_sec = (time_t) (useconds / ACE_ONE_SECOND_IN_USECS); + // Transforms one second in microseconds into nanoseconds. + elapsed_time.tv_nsec = (time_t) ((useconds % ACE_ONE_SECOND_IN_USECS) * 1000u + nseconds); +#else + elapsed_time.ts_sec = (time_t) (useconds / ACE_ONE_SECOND_IN_USECS); + // Transforms one second in microseconds into nanoseconds. + elapsed_time.ts_nsec = (time_t) ((useconds % ACE_ONE_SECOND_IN_USECS) * 1000u + nseconds); +#endif /* ACE_HAS_BROKEN_TIMESPEC_MEMBERS */ +} +#endif /* ACE_HAS_POSIX_TIME */ + +void +ACE_High_Res_Timer::elapsed_time_incr (ACE_Time_Value &tv) const +{ + hrtime_to_tv (tv, total_); +} + +void +ACE_High_Res_Timer::elapsed_time (ACE_hrtime_t &nanoseconds) const +{ + // Please do _not_ rearrange this equation. It is carefully + // designed and tested to avoid overflow on machines that don't have + // native 64-bit ints. In particular, division can be a problem. + // For more background on this, please see bugzilla #1024. +#if defined (ACE_WIN32) + nanoseconds = (this->end_ - this->start_) + * (1024000000u / ACE_High_Res_Timer::global_scale_factor()); +#else + nanoseconds = (this->end_ - this->start_) + * (1024000u / ACE_High_Res_Timer::global_scale_factor ()); +#endif /* ACE_WIN32 */ + // Caution - Borland has a problem with >>=, so resist the tempatation. + nanoseconds = nanoseconds >> 10; + // Right shift is implemented for non native 64-bit ints + // operator/ only for a 32 bit result ! +} + +void +ACE_High_Res_Timer::elapsed_time_incr (ACE_hrtime_t &nanoseconds) const +{ + // Same as above. +#if defined (ACE_WIN32) + nanoseconds = this->total_ + * (1024000000u / ACE_High_Res_Timer::global_scale_factor()); +#else + nanoseconds = this->total_ + * (1024000u / ACE_High_Res_Timer::global_scale_factor ()); +#endif + // Caution - Borland has a problem with >>=, so resist the tempatation. + nanoseconds = nanoseconds >> 10; +} + +#if !defined (ACE_HAS_WINCE) +void +ACE_High_Res_Timer::print_ave (const ACE_TCHAR *str, + const int count, + ACE_HANDLE handle) const +{ + ACE_TRACE ("ACE_High_Res_Timer::print_ave"); + + // Get the total number of nanoseconds elapsed. + ACE_hrtime_t total_nanoseconds; + this->elapsed_time (total_nanoseconds); + + // Separate to seconds and nanoseconds. + u_long total_secs = + ACE_static_cast (u_long, + total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS); + ACE_UINT32 extra_nsecs = + ACE_static_cast (ACE_UINT32, + total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS); + + ACE_TCHAR buf[100]; + if (count > 1) + { + ACE_hrtime_t avg_nsecs = total_nanoseconds / (ACE_UINT32) count; + ACE_OS::sprintf (buf, + ACE_LIB_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"), + count, + total_secs, + (extra_nsecs + 500u) / 1000u, + (u_long) ((avg_nsecs + 500u) / 1000u)); + } + else + ACE_OS::sprintf (buf, + ACE_LIB_TEXT (" total %3lu.%06lu secs\n"), + total_secs, + (extra_nsecs + 500lu) / 1000lu); + + ACE_OS::write (handle, + str, + ACE_OS::strlen (str)); + ACE_OS::write (handle, + buf, + ACE_OS::strlen (buf)); +} + +void +ACE_High_Res_Timer::print_total (const ACE_TCHAR *str, + const int count, + ACE_HANDLE handle) const +{ + ACE_TRACE ("ACE_High_Res_Timer::print_total"); + + // Get the total number of nanoseconds elapsed. + ACE_hrtime_t total_nanoseconds; + this->elapsed_time (total_nanoseconds); + + // Separate to seconds and nanoseconds. + u_long total_secs = + (u_long) (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS); + ACE_UINT32 extra_nsecs = + (ACE_UINT32) (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS); + + ACE_TCHAR buf[100]; + if (count > 1) + { + ACE_hrtime_t avg_nsecs = this->total_ / (ACE_UINT32) count; + + ACE_OS::sprintf (buf, + ACE_LIB_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"), + count, + total_secs, + (extra_nsecs + 500u) / 1000u, + (u_long) ((avg_nsecs + 500u) / 1000u)); + } + else + ACE_OS::sprintf (buf, + ACE_LIB_TEXT (" total %3lu.%06u secs\n"), + total_secs, + (extra_nsecs + 500u) / 1000u); + + ACE_OS::write (handle, + str, + ACE_OS::strlen (str)); + ACE_OS::write (handle, + buf, + ACE_OS::strlen (buf)); +} +#endif /* !ACE_HAS_WINCE */ + +int +ACE_High_Res_Timer::get_env_global_scale_factor (const ACE_TCHAR *env) +{ +#if !defined (ACE_HAS_WINCE) + if (env != 0) + { + const ACE_TCHAR *env_value = ACE_OS::getenv (env); + if (env_value != 0) + { + int value = ACE_OS::atoi (env_value); + if (value > 0) + { + ACE_High_Res_Timer::global_scale_factor (value); + return 0; + } + } + } +#else + ACE_UNUSED_ARG (env); +#endif /* !ACE_HAS_WINCE */ + return -1; +} diff --git a/ace/Timer/High_Res_Timer.h b/ace/Timer/High_Res_Timer.h new file mode 100644 index 00000000000..cffa4c07da8 --- /dev/null +++ b/ace/Timer/High_Res_Timer.h @@ -0,0 +1,308 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file High_Res_Timer.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_HIGH_RES_TIMER_H +#define ACE_HIGH_RES_TIMER_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/** + * @class ACE_High_Res_Timer + * + * @brief A high resolution timer class wrapper that encapsulates + * OS-specific high-resolution timers, such as those found on + * Solaris, AIX, Win32/Pentium, and VxWorks. + * + * Most of the member functions don't return values. The only + * reason that one would fail is if high-resolution time isn't + * supported on the platform. To avoid impacting performance + * and complicating the interface, in that case, + * is used instead. + * The global scale factor is required for platforms that have + * high-resolution timers that return units other than + * microseconds, such as clock ticks. It is represented as a + * static u_long, can only be accessed through static methods, + * and is used by all instances of High Res Timer. The member + * functions that return or print times use the global scale + * factor. They divide the "time" that they get from + * by global_scale_factor_ to obtain the + * time in microseconds. Its units are therefore 1/microsecond. + * On Windows the global_scale_factor_ units are 1/millisecond. + * There's a macro which gives the + * units/second. Because it's possible that the units/second + * changes in the future, it's recommended to use it instead + * of a "hard coded" solution. + * Dependend on the platform and used class members, there's a + * maximum elapsed period before overflow (which is not checked). + * Look at the documentation with some members functions. + * On some (most?) implementations it's not recommended to measure + * "long" timeperiods, because the error's can accumulate fast. + * This is probably not a problem profiling code, but could be + * on if the high resolution timer class is used to initiate + * actions after a "long" timeout. + * On Solaris, a scale factor of 1000 should be used because its + * high-resolution timer returns nanoseconds. However, on Intel + * platforms, we use RDTSC which returns the number of clock + * ticks since system boot. For a 200MHz cpu, each clock tick + * is 1/200 of a microsecond; the global_scale_factor_ should + * therefore be 200 or 200000 if it's in unit/millisecond. + * On Windows ::QueryPerformanceCounter() is used, which can be a + * different implementation depending on the used windows HAL + * (Hardware Abstraction Layer). On some it uses the PC "timer chip" + * while it uses RDTSC on others. + * NOTE: the elapsed time calculations in the print methods use + * ACE_hrtime_t values. Those methods do _not_ check for overflow! + * NOTE: Gabe raises this issue regarding + * : on multi-processors, the processor that + * you query for your value might not be the one + * you queried for . Its not clear how much + * divergence there would be, if any. + * This issue is not mentioned in the Solaris 2.5.1 gethrtime + * man page. + * A RDTSC NOTE: RDTSC is the Intel Pentium read-time stamp counter + * and is actualy a 64 bit clock cycle counter, which is increased + * with every cycle. It has a low overhead and can be read within + * 16 (pentium) or 32 (pentium II,III,...) cycles, but it doesn't + * serialize the processor, which could give wrong timings when + * profiling very short code fragments. + * Problematic is that some power sensitive devices + * (laptops for example, but probably also embeded devices), + * do change the cycle rate while running. + * Some pentiums can run on (at least) two clock frequency's. + * Another problem arises with multiprocessor computers, there + * are reports that the different RDTSC's are not always kept + * in sync. + * A windows "timer chip" NOTE: (8254-compatible real-time clock) + * When ::QueryPerformanceCounter() uses the 8254 it has a + * frequency off about 1.193 Mhz (or sometimes 3.579 Mhz?) and + * reading it requires some time (several thousand cycles). + */ +class ACE_Export ACE_High_Res_Timer +{ +public: + // = Initialization method. + + /** + * global_scale_factor_ is set to . All High_Res_Timers use + * global_scale_factor_. This allows applications to set the scale + * factor just once for all High_Res_Timers. Check + * High_Res_Timer.cpp for the default global_scale_factors for + * several platforms. For many platforms (e.g., Solaris), the + * global_scale_factor_ is set to 1000 so that need + * not be set. Careful, a of 0 will cause division + * by zero exceptions. + * Depending on the platform its units are 1/microsecond or + * 1/millisecond. Use inside calculations + * instead a hardcoded value. + */ + static void global_scale_factor (ACE_UINT32 gsf); + + /// Returns the global_scale_factor. + static ACE_UINT32 global_scale_factor (void); + + // On Win32, QueryPerformanceFrequency is used as a base for the global + // scale factor. The value this returns is often too small to be usefully + // converted to "ticks"/second - it loses unacceptably high levels of + // precision. So on Win32, global_scale_factor_ is in ticks/msec, not + // ticks/usec as on all others. +#if defined (ACE_WIN32) +# define ACE_HR_SCALE_CONVERSION (ACE_ONE_SECOND_IN_MSECS) +#else +# define ACE_HR_SCALE_CONVERSION (ACE_ONE_SECOND_IN_USECS) +#endif /* ACE_WIN32 */ + + /** + * Sets the global_scale_factor to the value in the + * environment variable. Returns 0 on success, -1 on failure. Note + * if points to string "0" (value zero), this call will fail. + * This is basically a no-op on CE because there is no concept of + * environment variable on CE. + */ + static int get_env_global_scale_factor (const ACE_TCHAR *env + = ACE_LIB_TEXT ("ACE_SCALE_FACTOR")); + + /** + * Set (and return, for info) the global scale factor by sleeping + * for and counting the number of intervening clock cycles. + * Average over of each. On some platforms, + * such as Pentiums, this is called automatically during the first + * ACE_High_Res_Timer construction with the default parameter + * values. An application can override that by calling calibrate + * with any desired parameter values _prior_ to constructing the + * first ACE_High_Res_Timer instance. + * Beware for platforms that can change the cycle rate on the fly. + */ + static ACE_UINT32 calibrate (const ACE_UINT32 usec = 500000, + const u_int iterations = 10); + + /// Initialize the timer. + ACE_High_Res_Timer (void); + + /// dtor. + ~ACE_High_Res_Timer (void); + + /// Reinitialize the timer. + void reset (void); + + /// Start timing. + void start (const ACE_OS::ACE_HRTimer_Op = ACE_OS::ACE_HRTIMER_GETTIME); + + /// Stop timing. + void stop (const ACE_OS::ACE_HRTimer_Op = ACE_OS::ACE_HRTIMER_GETTIME); + + /// Set to the number of microseconds elapsed. + /** + * Could overflow within hours on windows with emulated 64 bit int's + * and a fast counter. VC++ and Borland normaly use __int64 and + * so normaly don't have this problem. + */ + void elapsed_time (ACE_Time_Value &tv) const; + + /// Set to the number of nanoseconds elapsed. + /** + * Will overflow when measuring more than 194 day's. + */ + void elapsed_time (ACE_hrtime_t &nanoseconds) const; + +#if defined (ACE_HAS_POSIX_TIME) + /// Returns the elapsed (stop - start) time in a struct timespec + /// (sec, nsec). + void elapsed_time (struct timespec &) const; +#endif /* ACE_HAS_POSIX_TIME */ + + /// Sets to the elapsed (stop - start) time in microseconds. + /** + * Will overflow on windows when measuring more than appox. 2^^54 ticks. + * Is still more than 48 days with a 4 Ghz counter. + */ + void elapsed_microseconds (ACE_hrtime_t &usecs) const; + + /// Start incremental timing. + void start_incr (const ACE_OS::ACE_HRTimer_Op = ACE_OS::ACE_HRTIMER_GETTIME); + + /// Stop incremental timing. + void stop_incr (const ACE_OS::ACE_HRTimer_Op = ACE_OS::ACE_HRTIMER_GETTIME); + + /// Set to the number of microseconds elapsed between all calls + /// to start_incr and stop_incr. + void elapsed_time_incr (ACE_Time_Value &tv) const; + + /// Set to the number of nanoseconds elapsed between all calls + /// to start_incr and stop_incr. + void elapsed_time_incr (ACE_hrtime_t &nanoseconds) const; + +#if !defined (ACE_HAS_WINCE) + // @@ WINCE These two functions are currently not supported on Windows CE. + // However, we should probably use the handle and ACE_Log_Msg to + // print out the result. + /// Print total time. NOTE: only use if incremental + /// timings had been used! + void print_total (const ACE_TCHAR *message, + const int iterations = 1, + ACE_HANDLE handle = ACE_STDOUT) const; + + /// Print average time. + void print_ave (const ACE_TCHAR *message, + const int iterations = 1, + ACE_HANDLE handle = ACE_STDOUT) const; +#endif /* !ACE_HAS_WINCE */ + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + /** + * Get the current "time" as the high resolution counter at this time. + * This is intended to be useful for supplying to a ACE_Timer_Queue + * as the gettimeofday function, thereby basing the timer calculations + * on the high res timer rather than wall clock time. + */ + static ACE_Time_Value gettimeofday_hr (void); + + /** + * THIS FUNCTION IS DEPRECATED. PLEASE USE + * INSTEAD! Calls passing + * . This function can be used to parameterize + * objects such as . If + * is not set, and we're on a platform that + * requires (e.g., Win32), + * ACE_OS::gettimeofday will be used instead of . + * This allows applications on Intel to use even + * when is not set. However, setting the + * appropriately will result in the finest + * resolution possible. + */ + static ACE_Time_Value gettimeofday (const ACE_OS::ACE_HRTimer_Op = + ACE_OS::ACE_HRTIMER_GETTIME); + + /// Converts an to using global_scale_factor_. + static void hrtime_to_tv (ACE_Time_Value &tv, + const ACE_hrtime_t hrt); + +#if defined (linux) + /** + * This is used to find out the Mhz of the machine for the scale + * factor. If there are any problems getting it, we just return 1 + * (the default). + */ + static ACE_UINT32 get_cpuinfo (void); +#endif /* defined (linux) */ + +private: + /** + * For internal use: gets the high-resolution time using + * . Except on platforms that require that the + * be set, such as ACE_WIN32, uses the + * low-resolution clock if the has not been + * set. + */ + static ACE_hrtime_t gettime (const ACE_OS::ACE_HRTimer_Op = + ACE_OS::ACE_HRTIMER_GETTIME); + + /// Starting time. + ACE_hrtime_t start_; + + /// Ending time. + ACE_hrtime_t end_; + + /// Total elapsed time. + ACE_hrtime_t total_; + + /// Start time of incremental timing. + ACE_hrtime_t start_incr_; + + /// Converts ticks to microseconds. That is, ticks / + /// global_scale_factor_ == microseconds. + static ACE_UINT32 global_scale_factor_; + + /** + * Indicates the status of the global scale factor, + * 0 = hasn't been set + * 1 = been set + * -1 = HR timer not supported + */ + static int global_scale_factor_status_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/High_Res_Timer.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_HIGH_RES_TIMER_H */ diff --git a/ace/Timer/High_Res_Timer.i b/ace/Timer/High_Res_Timer.i new file mode 100644 index 00000000000..34c494dce16 --- /dev/null +++ b/ace/Timer/High_Res_Timer.i @@ -0,0 +1,158 @@ +/* -*- C++ -*- */ +// $Id$ + +// Be very carefull before changing the calculations inside +// ACE_High_Res_Timer. The precision matters and we are using integer +// calculations not floating point. Also look good at the emulated 64 +// bit int class (inside Basic_Types{h,i,cpp} before changing +// anything. It's operator/ only returns 32 bits not 64 bits, among +// other things. + +ACE_INLINE void +ACE_High_Res_Timer::hrtime_to_tv (ACE_Time_Value &tv, + const ACE_hrtime_t hrt) +{ + // The following are based on the units of global_scale_factor_ + // being 1/microsecond. Therefore, dividing by it converts + // clock ticks to microseconds. + tv.sec ((long) (hrt / (ACE_UINT32) ACE_HR_SCALE_CONVERSION / + global_scale_factor ())); + + // Calculate usec in a manner that's compatible with ACE_U_LongLong. + // hrt = (tv.sec * ACE_ONE_SECOND_IN_USECS + tv.usec) * global_scale_factor_ + // tv.usec = hrt / global_scale_factor_ - tv.sec * ACE_ONE_SECOND_IN_USECS + // That first term will be lossy, so factor out global_scale_factor_: + // tv.usec = (hrt - tv.sec * ACE_ONE_SECOND_IN_USECS * global_scale_factor_)/ + // global_scale_factor + ACE_hrtime_t tmp = tv.sec (); + tmp *= ((ACE_UINT32) ACE_HR_SCALE_CONVERSION * global_scale_factor ()); +#if defined (ACE_WIN32) + // Win32's scale factor is in ticks/msec, so multiply up to usec. + ACE_hrtime_t subsec = hrt - tmp; // Remainder of ticks < 1sec + ACE_UINT32 msec = (ACE_UINT32) (subsec / global_scale_factor ()); // #msec + ACE_hrtime_t usec64 = subsec - (msec * global_scale_factor ()); +# if defined (ACE_LACKS_LONGLONG_T) + ACE_UINT32 usec = usec64.lo(); +# else + ACE_UINT32 usec = (ACE_UINT32) usec64; +# endif // ACE_LACKS_LONGLONG_T + // (tick * usec/msec) / tick/msec = usec + usec = (usec * 1000) / (ACE_UINT32) global_scale_factor (); + tv.usec ((msec * 1000) + usec); +#else + tv.usec ((long) ((hrt - tmp) / global_scale_factor ())); +#endif +} + + +ACE_INLINE ACE_Time_Value +ACE_High_Res_Timer::gettimeofday (const ACE_OS::ACE_HRTimer_Op op) +{ +#if defined (ACE_WIN32) + // Get the global scale factor if there isn't one yet. + if (ACE_High_Res_Timer::global_scale_factor_status_ == 0) + ACE_High_Res_Timer::global_scale_factor (); + + // If there isn't a high-res timer, use gettimeofday (); + if (ACE_High_Res_Timer::global_scale_factor_status_ == -1) + return ACE_OS::gettimeofday (); +#endif /* ACE_WIN32 */ + + ACE_Time_Value tv; + ACE_High_Res_Timer::hrtime_to_tv (tv, + ACE_OS::gethrtime (op)); + return tv; +} + + +// Get the current high res timer as the time of day. This is intended +// to be used for a gettimeofday replacement in ACE_Timer_Queue and +// derived classes so the timers will bebased on high res timers rather +// than wall clock time. It uses the ACE_High_Res_Timer::gettimeofday +// function, which is deprecated. If it gets removed, please move the +// code down here, intact. +ACE_INLINE ACE_Time_Value +ACE_High_Res_Timer::gettimeofday_hr (void) +{ + return ACE_High_Res_Timer::gettimeofday (); +} + + +ACE_INLINE ACE_hrtime_t +ACE_High_Res_Timer::gettime (const ACE_OS::ACE_HRTimer_Op op) +{ +#if defined (ACE_WIN32) + // Get the global scale factor if there isn't one yet. + if (ACE_High_Res_Timer::global_scale_factor_status_ == 0) + ACE_High_Res_Timer::global_scale_factor (); + + // If there isn't a high-res timer, use gettimeofday (); + if (ACE_High_Res_Timer::global_scale_factor_status_ == -1) + { + ACE_Time_Value tv = ACE_OS::gettimeofday (); + // Return the time in microseconds because the global_scale_factor_ + // is 1. + return tv.sec () * ACE_ONE_SECOND_IN_USECS + tv.usec (); + } +#endif /* ACE_WIN32 */ + + return ACE_OS::gethrtime (op); +} + +ACE_INLINE +ACE_High_Res_Timer::~ACE_High_Res_Timer (void) +{ +} + +ACE_INLINE void +ACE_High_Res_Timer::start (const ACE_OS::ACE_HRTimer_Op op) +{ + ACE_TRACE ("ACE_High_Res_Timer::start"); + this->start_ = ACE_High_Res_Timer::gettime (op); +} + +ACE_INLINE void +ACE_High_Res_Timer::stop (const ACE_OS::ACE_HRTimer_Op op) +{ + ACE_TRACE ("ACE_High_Res_Timer::stop"); + this->end_ = ACE_High_Res_Timer::gettime (op); +} + +ACE_INLINE void +ACE_High_Res_Timer::start_incr (const ACE_OS::ACE_HRTimer_Op op) +{ + ACE_TRACE ("ACE_High_Res_Timer::start_incr"); + this->start_incr_ = ACE_High_Res_Timer::gettime (op); +} + +ACE_INLINE void +ACE_High_Res_Timer::stop_incr (const ACE_OS::ACE_HRTimer_Op op) +{ + ACE_TRACE ("ACE_High_Res_Timer::stop_incr"); + this->total_ += ACE_High_Res_Timer::gettime (op) - this->start_incr_; +} + +ACE_INLINE void +ACE_High_Res_Timer::elapsed_microseconds (ACE_hrtime_t &usecs) const +{ +#if defined (ACE_WIN32) + // Win32 scale factor is in msec + // This could give overflow when measuring a long time with a + // big global_scale_factor() (> 48 days with a 4Ghz tick freq.) + // To be looked after in the future. + usecs = (ACE_hrtime_t) (((this->end_ - this->start_) * 1000) / + global_scale_factor ()); +#else + usecs = + (ACE_hrtime_t) ((this->end_ - this->start_) / global_scale_factor ()); +#endif + + + +} + +ACE_INLINE void +ACE_High_Res_Timer::global_scale_factor (ACE_UINT32 gsf) +{ + global_scale_factor_ = gsf; +} diff --git a/ace/Timer/Profile_Timer.cpp b/ace/Timer/Profile_Timer.cpp new file mode 100644 index 00000000000..2f4ef9f8f87 --- /dev/null +++ b/ace/Timer/Profile_Timer.cpp @@ -0,0 +1,418 @@ +// $Id$ + +#include "ace/Profile_Timer.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +# include "ace/Profile_Timer.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Profile_Timer, "$Id$") + +ACE_ALLOC_HOOK_DEFINE(ACE_Profile_Timer) + +#if (defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE)) && !defined (ACE_WIN32) + +void +ACE_Profile_Timer::dump (void) const +{ + ACE_TRACE ("ACE_Profile_Timer::dump"); +} + +// Initialize interval timer. + +ACE_Profile_Timer::ACE_Profile_Timer (void) +{ + ACE_TRACE ("ACE_Profile_Timer::ACE_Profile_Timer"); + ACE_OS::memset (&this->end_usage_, 0, sizeof this->end_usage_); + ACE_OS::memset (&this->begin_usage_, 0, sizeof this->begin_usage_); + ACE_OS::memset (&this->last_usage_, 0, sizeof this->last_usage_); + +# if defined (ACE_HAS_PRUSAGE_T) + ACE_OS::memset (&this->last_usage_, 0, sizeof this->last_usage_); + char buf[20]; + ACE_OS::sprintf (buf, "/proc/%d", ACE_static_cast (int, ACE_OS::getpid ())); + + this->proc_handle_ = ACE_OS::open (buf, O_RDONLY, 0); + if (this->proc_handle_ == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + buf)); +# elif defined (ACE_HAS_GETRUSAGE) + ACE_OS::memset (&this->begin_time_, 0, sizeof this->begin_time_); + ACE_OS::memset (&this->end_time_, 0, sizeof this->end_time_); + ACE_OS::memset (&this->last_time_, 0, sizeof this->last_time_); +# endif /* ACE_HAS_PRUSAGE_T */ +} + +// Terminate the interval timer. +ACE_Profile_Timer::~ACE_Profile_Timer (void) +{ + ACE_TRACE ("ACE_Profile_Timer::~ACE_Profile_Timer"); +# if defined (ACE_HAS_PRUSAGE_T) + if (ACE_OS::close (this->proc_handle_) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_Profile_Timer::~ACE_Profile_Timer"))); +# endif /* ACE_HAS_PRUSAGE_T */ +} + +// Return the resource utilization. + +void +ACE_Profile_Timer::get_rusage (ACE_Profile_Timer::Rusage &usage) +{ + ACE_TRACE ("ACE_Profile_Timer::get_rusage"); + usage = this->end_usage_; +} + +# if defined (ACE_HAS_PRUSAGE_T) + +// Compute the amount of resource utilization since the start time. + +void +ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &rusage) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage"); + rusage.pr_lwpid = + this->end_usage_.pr_lwpid - this->last_usage_.pr_lwpid; + rusage.pr_count = + this->end_usage_.pr_count - this->last_usage_.pr_count; + rusage.pr_minf = + this->end_usage_.pr_minf - this->last_usage_.pr_minf; + rusage.pr_majf = + this->end_usage_.pr_majf - this->last_usage_.pr_majf; + rusage.pr_inblk = + this->end_usage_.pr_inblk - this->last_usage_.pr_inblk; + rusage.pr_oublk = + this->end_usage_.pr_oublk - this->last_usage_.pr_oublk; + rusage.pr_msnd = + this->end_usage_.pr_msnd - this->last_usage_.pr_msnd; + rusage.pr_mrcv = + this->end_usage_.pr_mrcv - this->last_usage_.pr_mrcv; + rusage.pr_sigs = + this->end_usage_.pr_sigs - this->last_usage_.pr_sigs; + this->subtract (rusage.pr_wtime, + this->end_usage_.pr_wtime, + this->last_usage_.pr_wtime); + this->subtract (rusage.pr_ltime, + this->end_usage_.pr_ltime, + this->last_usage_.pr_ltime); + this->subtract (rusage.pr_slptime, + this->end_usage_.pr_slptime, + this->last_usage_.pr_slptime); + rusage.pr_vctx = + this->end_usage_.pr_vctx - this->last_usage_.pr_vctx; + rusage.pr_ictx = + this->end_usage_.pr_ictx - this->last_usage_.pr_ictx; + rusage.pr_sysc = + this->end_usage_.pr_sysc - this->last_usage_.pr_sysc; + rusage.pr_ioch = + this->end_usage_.pr_ioch - this->last_usage_.pr_ioch; +} + +// Compute the elapsed time. + +void +ACE_Profile_Timer::compute_times (ACE_Elapsed_Time &et) +{ + ACE_TRACE ("ACE_Profile_Timer::compute_times"); + timespec_t td; + + ACE_Profile_Timer::Rusage &end = this->end_usage_; + ACE_Profile_Timer::Rusage &begin = this->begin_usage_; + + this->subtract (td, end.pr_tstamp, begin.pr_tstamp); + // Convert nanoseconds into seconds. + et.real_time = td.tv_sec + ((double) td.tv_nsec) / ACE_ONE_SECOND_IN_NSECS; + this->subtract (td, end.pr_utime, begin.pr_utime); + // Convert nanoseconds into seconds. + et.user_time = td.tv_sec + ((double) td.tv_nsec) / ACE_ONE_SECOND_IN_NSECS; + this->subtract (td, end.pr_stime, begin.pr_stime); + // Convert nanoseconds into seconds. + et.system_time = td.tv_sec + ((double) td.tv_nsec) / ACE_ONE_SECOND_IN_NSECS; +} + +// Determine the difference between T1 and T2. + +void +ACE_Profile_Timer::subtract (timespec_t &tdiff, timespec_t &t1, timespec_t &t0) +{ + ACE_TRACE ("ACE_Profile_Timer::subtract"); + tdiff.tv_sec = t1.tv_sec - t0.tv_sec; + tdiff.tv_nsec = t1.tv_nsec - t0.tv_nsec; + + // Normalize the time. + + while (tdiff.tv_nsec < 0) + { + tdiff.tv_sec--; + tdiff.tv_nsec += ACE_ONE_SECOND_IN_NSECS; + } +} + +# elif defined (ACE_HAS_GETRUSAGE) +// Compute the amount of resource utilization since the start time. + +void +ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &usage) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage"); +# if !defined (ACE_HAS_LIMITED_RUSAGE_T) + // integral shared memory size + usage.ru_ixrss = + this->end_usage_.ru_ixrss - this->last_usage_.ru_ixrss; + // integral unshared data " + usage.ru_idrss = + this->end_usage_.ru_idrss - this->last_usage_.ru_idrss; + // integral unshared stack " + usage.ru_isrss = + this->end_usage_.ru_isrss - this->last_usage_.ru_isrss; + // page reclaims - total vmfaults + usage.ru_minflt = + this->end_usage_.ru_minflt - this->last_usage_.ru_minflt; + // page faults + usage.ru_majflt = + this->end_usage_.ru_majflt - this->last_usage_.ru_majflt; + // swaps + usage.ru_nswap = + this->end_usage_.ru_nswap - this->last_usage_.ru_nswap; + // block input operations + usage.ru_inblock = + this->end_usage_.ru_inblock - this->last_usage_.ru_inblock; + // block output operations + usage.ru_oublock = + this->end_usage_.ru_oublock - this->last_usage_.ru_oublock; + // messages sent + usage.ru_msgsnd = + this->end_usage_.ru_msgsnd - this->last_usage_.ru_msgsnd; + // messages received + usage.ru_msgrcv = + this->end_usage_.ru_msgrcv - this->last_usage_.ru_msgrcv; + // signals received + usage.ru_nsignals = + this->end_usage_.ru_nsignals - this->last_usage_.ru_nsignals; + // voluntary context switches + usage.ru_nvcsw = + this->end_usage_.ru_nvcsw - this->last_usage_.ru_nvcsw; + // involuntary context switches + usage.ru_nivcsw = + this->end_usage_.ru_nivcsw - this->last_usage_.ru_nivcsw; + this->subtract (usage.ru_utime, + this->end_usage_.ru_utime, + this->last_usage_.ru_utime); + this->subtract (usage.ru_stime, + this->end_usage_.ru_stime, + this->last_usage_.ru_stime); +# else + ACE_UNUSED_ARG(usage); +# endif /* ACE_HAS_LIMITED_RUSAGE_T */ +} + +void +ACE_Profile_Timer::compute_times (ACE_Elapsed_Time &et) +{ + ACE_TRACE ("ACE_Profile_Timer::compute_times"); + + timeval td; + + this->subtract (td, this->end_time_, this->begin_time_); + et.real_time = td.tv_sec + ((double) td.tv_usec) / ACE_ONE_SECOND_IN_USECS; + + this->subtract (td, this->end_usage_.ru_utime, this->begin_usage_.ru_utime); + et.user_time = td.tv_sec + ((double) td.tv_usec) / ACE_ONE_SECOND_IN_USECS; + + this->subtract (td, this->end_usage_.ru_stime, this->begin_usage_.ru_stime); + et.system_time = td.tv_sec + ((double) td.tv_usec) / ACE_ONE_SECOND_IN_USECS; +} + +// Determine the difference between T1 and T2. + +void +ACE_Profile_Timer::subtract (timeval &tdiff, timeval &t1, timeval &t0) +{ + ACE_TRACE ("ACE_Profile_Timer::subtract"); + tdiff.tv_sec = t1.tv_sec - t0.tv_sec; + tdiff.tv_usec = t1.tv_usec - t0.tv_usec; + + // Normalize the time. + + while (tdiff.tv_usec < 0) + { + tdiff.tv_sec--; + tdiff.tv_usec += ACE_ONE_SECOND_IN_USECS; + } +} + +# endif /* ACE_HAS_PRUSAGE_T */ + +// Compute the amount of time that has elapsed between start and stop. + +int +ACE_Profile_Timer::elapsed_time (ACE_Elapsed_Time &et) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_time"); + this->compute_times (et); + return 0; +} + +#elif defined (ACE_WIN32) /* defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE) */ + +void +ACE_Profile_Timer::dump (void) const +{ + ACE_TRACE ("ACE_Profile_Timer::dump"); + timer_.dump (); +} + +// Initialize interval timer. +ACE_Profile_Timer::ACE_Profile_Timer (void) + : timer_ () +{ + ACE_TRACE ("ACE_Profile_Timer::ACE_Profile_Timer"); +# if defined (ACE_HAS_GETRUSAGE) + + ACE_OS::memset (&this->end_usage_, 0, sizeof this->end_usage_); + ACE_OS::memset (&this->begin_usage_, 0, sizeof this->begin_usage_); + ACE_OS::memset (&this->last_usage_, 0, sizeof this->last_usage_); + + ACE_OS::memset (&this->begin_time_, 0, sizeof this->begin_time_); + ACE_OS::memset (&this->end_time_, 0, sizeof this->end_time_); + ACE_OS::memset (&this->last_time_, 0, sizeof this->last_time_); +# endif /* ACE_HAS_GETRUSAGE */ +} + +int +ACE_Profile_Timer::elapsed_time (ACE_Elapsed_Time &et) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_time"); + + ACE_hrtime_t delta_t; // nanoseconds + timer_.elapsed_time (delta_t); +# if defined (ACE_LACKS_LONGLONG_T) + et.real_time = delta_t / (double) ACE_ONE_SECOND_IN_NSECS; +# else + et.real_time = (__int64) delta_t / (double) ACE_ONE_SECOND_IN_NSECS; +# endif /* ACE_LACKS_LONGLONG_T */ +# if defined (ACE_HAS_GETRUSAGE) + ACE_Time_Value atv = ACE_Time_Value (this->end_usage_.ru_utime) + - ACE_Time_Value (this->begin_usage_.ru_utime); + et.user_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS; + + atv = ACE_Time_Value (this->end_usage_.ru_stime) + - ACE_Time_Value (this->begin_usage_.ru_stime); + et.system_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS; +# else /* ACE_HAS_GETRUSAGE */ + et.user_time = 0; + et.system_time = 0; +# endif /* ACE_HAS_GETRUSAGE */ + + return 0; +} + +// Return the resource utilization. + +void +ACE_Profile_Timer::get_rusage (ACE_Profile_Timer::Rusage &usage) +{ + ACE_TRACE ("ACE_Profile_Timer::get_rusage"); +# if defined (ACE_HAS_GETRUSAGE) + usage = this->end_usage_; +# else /* ACE_HAS_GETRUSAGE */ + usage = 0; +# endif /* ACE_HAS_GETRUSAGE */ +} + +// Compute the amount of resource utilization since the start time. + +void +ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &usage) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage"); + +# if defined (ACE_HAS_GETRUSAGE) + usage.ru_utime = + this->end_usage_.ru_utime - this->begin_usage_.ru_utime; + usage.ru_stime = + this->end_usage_.ru_stime - this->begin_usage_.ru_stime; +# else /* ACE_HAS_GETRUSAGE */ + usage = 0; +# endif /* ACE_HAS_GETRUSAGE */ +} + +# if defined (ACE_HAS_GETRUSAGE) +// Determine the difference between T1 and T2. + +void +ACE_Profile_Timer::subtract (timeval &tdiff, timeval &t1, timeval &t0) +{ + ACE_TRACE ("ACE_Profile_Timer::subtract"); + tdiff.tv_sec = t1.tv_sec - t0.tv_sec; + tdiff.tv_usec = t1.tv_usec - t0.tv_usec; + + // Normalize the time. + + while (tdiff.tv_usec < 0) + { + tdiff.tv_sec--; + tdiff.tv_usec += ACE_ONE_SECOND_IN_USECS; + } +} +# endif /* ACE_HAS_GETRUSAGE */ + +#else + +void +ACE_Profile_Timer::dump (void) const +{ + ACE_TRACE ("ACE_Profile_Timer::dump"); + timer_.dump (); +} + +ACE_Profile_Timer::ACE_Profile_Timer (void) + : timer_ () +{ + ACE_TRACE ("ACE_Profile_Timer::ACE_Profile_Timer"); +} + +int +ACE_Profile_Timer::elapsed_time (ACE_Elapsed_Time &et) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_time"); + +# if defined (ACE_LACKS_FLOATING_POINT) + ACE_Time_Value delta_t; /* elapsed time will be in microseconds */ + timer_.elapsed_time (delta_t); + + // Store the time in usecs. + et.real_time = delta_t.sec () * ACE_ONE_SECOND_IN_USECS + + delta_t.usec (); +# else /* ! ACE_LACKS_FLOATING_POINT */ + ACE_hrtime_t delta_t; /* nanoseconds */ + timer_.elapsed_time (delta_t); + + et.real_time = delta_t / (double) ACE_ONE_SECOND_IN_NSECS; +# endif /* ! ACE_LACKS_FLOATING_POINT */ + + et.user_time = 0; + et.system_time = 0; + + return 0; +} + +void +ACE_Profile_Timer::get_rusage (ACE_Profile_Timer::Rusage &usage) +{ + ACE_TRACE ("ACE_Profile_Timer::get_rusage"); + usage = 0; +} + + +void +ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &usage) +{ + ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage"); + usage = 0; +} + +#endif /* defined (ACE_HAS_PRUSAGE_T) || + defined (ACE_HAS_GETRUSAGE) && !defined (ACE_WIN32) */ diff --git a/ace/Timer/Profile_Timer.h b/ace/Timer/Profile_Timer.h new file mode 100644 index 00000000000..8bdacb52fbf --- /dev/null +++ b/ace/Timer/Profile_Timer.h @@ -0,0 +1,133 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Profile_Timer.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + + +#ifndef ACE_PROFILE_TIMER_H +#define ACE_PROFILE_TIMER_H +#include "ace/pre.h" + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Time_Value.h" +#include "ace/High_Res_Timer.h" + +/** + * @class ACE_Profile_Timer + * + * @brief This class provides both a timing mechanism and a mechanism + * for reporting the resource usage of a process. + */ +class ACE_Export ACE_Profile_Timer +{ +public: + + /** + * @class ACE_Elapsed_Time + * + * @brief Keeps track of the various user, system, and elapsed (real) + * times. + * + * If is enabled these values are in + * microseconds, otherwise, they are in seconds. + */ + class ACE_Elapsed_Time + { + public: + ACE_timer_t real_time; + ACE_timer_t user_time; + ACE_timer_t system_time; + }; + + typedef ACE_Rusage Rusage; + + // = Initialization and termination methods. + /// Default constructor. + ACE_Profile_Timer (void); + + /// Shutdown the timer. + ~ACE_Profile_Timer (void); + + // = Timer methods. + /// Activate the timer. + int start (void); + + /// Stop the timer. + int stop (void); + + // = Resource utilization methods. + /// Compute the time elapsed since . + int elapsed_time (ACE_Elapsed_Time &et); + + /// Compute the amount of resource utilization since the start time. + void elapsed_rusage (ACE_Profile_Timer::Rusage &rusage); + + /// Return the resource utilization (don't recompute it). + void get_rusage (ACE_Profile_Timer::Rusage &rusage); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Compute how much time has elapsed. + void compute_times (ACE_Elapsed_Time &et); + + /// Keep track of the starting resource utilization. + ACE_Profile_Timer::Rusage begin_usage_; + + /// Keep track of the ending resource utilization. + ACE_Profile_Timer::Rusage end_usage_; + + /// Keep track of the last rusage for incremental timing. + ACE_Profile_Timer::Rusage last_usage_; + +#if defined (ACE_HAS_PRUSAGE_T) + /// Substract two timestructs and store their difference. + void subtract (timespec_t &tdiff, timespec_t &t0, timespec_t &t1); + + /// I/O handle for /proc file system. + ACE_HANDLE proc_handle_; + +#elif defined (ACE_HAS_GETRUSAGE) + /// Substract two timestructs and store their difference. + void subtract (timeval &tdiff, + timeval &t0, + timeval &t1); + + /// Keep track of the beginning time. + timeval begin_time_; + + /// Keep track of the ending time. + timeval end_time_; + + /// Keep track of the last time for incremental timing. + timeval last_time_; +#endif /* ACE_HAS_PRUSAGE_T */ + +#if defined (ACE_WIN32) || (!defined (ACE_HAS_PRUSAGE_T) && !defined (ACE_HAS_GETRUSAGE)) + /// The high resolution timer + ACE_High_Res_Timer timer_; +#endif /* ACE_WIN32 || !ACE_HAS_PRUSAGE_T && !ACE_HAS_GETRUSAGE */ +}; + +#if defined (__ACE_INLINE__) +# include "ace/Profile_Timer.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /* ACE_PROFILE_TIMER_H */ diff --git a/ace/Timer/Profile_Timer.i b/ace/Timer/Profile_Timer.i new file mode 100644 index 00000000000..e77af9b1686 --- /dev/null +++ b/ace/Timer/Profile_Timer.i @@ -0,0 +1,104 @@ +/* -*- C++ -*- */ +// $Id$ + +#if (defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE)) && !defined (ACE_WIN32) + +# if defined (ACE_HAS_PRUSAGE_T) +ACE_INLINE int +ACE_Profile_Timer::start (void) +{ + ACE_TRACE ("ACE_Profile_Timer::start"); + return ACE_OS::ioctl (this->proc_handle_, + PIOCUSAGE, + &this->begin_usage_); +} + +ACE_INLINE int +ACE_Profile_Timer::stop (void) +{ + ACE_TRACE ("ACE_Profile_Timer::stop"); + this->last_usage_ = this->end_usage_; + return ACE_OS::ioctl (this->proc_handle_, + PIOCUSAGE, + &this->end_usage_); +} +# elif defined (ACE_HAS_GETRUSAGE) +ACE_INLINE int +ACE_Profile_Timer::start (void) +{ + ACE_TRACE ("ACE_Profile_Timer::start"); + this->begin_time_ = ACE_OS::gettimeofday (); + ACE_OS::getrusage (RUSAGE_SELF, + &this->begin_usage_); + return 0; +} + +ACE_INLINE int +ACE_Profile_Timer::stop (void) +{ + ACE_TRACE ("ACE_Profile_Timer::stop"); + this->last_time_ = this->end_time_; + this->end_time_ = ACE_OS::gettimeofday (); + this->last_usage_ = this->end_usage_; + ACE_OS::getrusage (RUSAGE_SELF, + &this->end_usage_); + return 0; +} + +# endif /* ACE_HAS_PRUSAGE_T */ + +#elif defined (ACE_WIN32) + +ACE_INLINE +ACE_Profile_Timer::~ACE_Profile_Timer (void) +{ +} + +ACE_INLINE int +ACE_Profile_Timer::start (void) +{ + ACE_TRACE ("ACE_Profile_Timer::start"); +# if defined (ACE_HAS_GETRUSAGE) + ACE_OS::getrusage (RUSAGE_SELF, + &this->begin_usage_); +# endif /* ACE_HAS_GETRUSAGE */ + this->timer_.start (); + return 0; +} + +ACE_INLINE int +ACE_Profile_Timer::stop (void) +{ + ACE_TRACE ("ACE_Profile_Timer::stop"); + this->timer_.stop (); +# if defined (ACE_HAS_GETRUSAGE) + this->last_usage_ = this->end_usage_; + ACE_OS::getrusage (RUSAGE_SELF, &this->end_usage_); +# endif /* ACE_HAS_GETRUSAGE */ + return 0; +} + +#else + +ACE_INLINE int +ACE_Profile_Timer::start (void) +{ + ACE_TRACE ("ACE_Profile_Timer::start"); + this->timer_.start (); + return 0; +} + +ACE_INLINE int +ACE_Profile_Timer::stop (void) +{ + ACE_TRACE ("ACE_Profile_Timer::stop"); + this->timer_.stop (); + return 0; +} + +ACE_INLINE +ACE_Profile_Timer::~ACE_Profile_Timer (void) +{ +} + +#endif /* defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE) */ diff --git a/ace/Timer/System_Time.cpp b/ace/Timer/System_Time.cpp new file mode 100644 index 00000000000..9e2f2b69380 --- /dev/null +++ b/ace/Timer/System_Time.cpp @@ -0,0 +1,141 @@ +// System_Time.cpp +// $Id$ + +#include "ace/System_Time.h" + +ACE_RCSID(ace, System_Time, "$Id$") + +ACE_System_Time::ACE_System_Time (const ACE_TCHAR *poolname) + : delta_time_ (0) +{ + ACE_TRACE ("ACE_System_Time::ACE_System_Time"); + + // Only create a new unique filename for the memory pool file + // if the user didn't supply one... + if (poolname == 0) + { +#if defined (ACE_DEFAULT_BACKING_STORE) + // Create a temporary file. + ACE_OS::strcpy (this->poolname_, + ACE_DEFAULT_BACKING_STORE); +#else /* ACE_DEFAULT_BACKING_STORE */ + if (ACE_Lib_Find::get_temp_dir (this->poolname_, + MAXPATHLEN - 17) == -1) + // -17 for ace-malloc-XXXXXX + { + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("Temporary path too long, ") + ACE_LIB_TEXT ("defaulting to current directory\n"))); + this->poolname_[0] = 0; + } + + // Add the filename to the end + ACE_OS::strcat (this->poolname_, ACE_LIB_TEXT ("ace-malloc-XXXXXX")); + +#endif /* ACE_DEFAULT_BACKING_STORE */ + } + else + ACE_OS::strsncpy (this->poolname_, + poolname, + (sizeof this->poolname_ / sizeof (ACE_TCHAR))); + + ACE_NEW (this->shmem_, + ALLOCATOR (this->poolname_)); +} + +ACE_System_Time::~ACE_System_Time (void) +{ + ACE_TRACE ("ACE_System_Time::~ACE_System_Time"); + delete this->shmem_; +} + +// Get the local system time. + +int +ACE_System_Time::get_local_system_time (ACE_UINT32 &time_out) +{ + ACE_TRACE ("ACE_System_Time::get_local_system_time"); + time_out = ACE_OS::time (0); + return 0; +} + +int +ACE_System_Time::get_local_system_time (ACE_Time_Value &time_out) +{ + ACE_TRACE ("ACE_System_Time::get_local_system_time"); + time_out.sec (ACE_OS::time (0)); + return 0; +} + +// Get the system time of the central time server. + +int +ACE_System_Time::get_master_system_time (ACE_UINT32 &time_out) +{ + ACE_TRACE ("ACE_System_Time::get_master_system_time"); + + if (this->delta_time_ == 0) + { + // Try to find it + void * temp; + if (this->shmem_->find (ACE_DEFAULT_TIME_SERVER_STR, temp) == -1) + { + // No time entry in shared memory (meaning no Clerk exists) + // so return the local time of the host. + return this->get_local_system_time (time_out); + } + else + // Extract the delta time. + this->delta_time_ = (long *) temp; + } + + ACE_UINT32 local_time; + + // If delta_time is positive, it means that the system clock is + // ahead of our local clock so add delta to the local time to get an + // approximation of the system time. Else if delta time is negative, + // it means that our local clock is ahead of the system clock, so + // return the last local time stored (to avoid time conflicts). + if (*this->delta_time_ >=0 ) + { + this->get_local_system_time (local_time); + time_out = local_time + (ACE_UINT32) *this->delta_time_; + } + else + // Return the last local time. Note that this is stored as the + // second field in shared memory. + time_out = *(this->delta_time_ + 1); + return 0; +} + +int +ACE_System_Time::get_master_system_time (ACE_Time_Value &time_out) +{ + ACE_TRACE ("ACE_System_Time::get_master_system_time"); + ACE_UINT32 to; + if (this->get_master_system_time (to) == -1) + return -1; + time_out.sec (to); + return 0; +} + +// Synchronize local system time with the central time server using +// specified mode (currently unimplemented). + +int +ACE_System_Time::sync_local_system_time (ACE_System_Time::Sync_Mode) +{ + ACE_TRACE ("ACE_System_Time::sync_local_system_time"); + ACE_NOTSUP_RETURN (-1); +} + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Malloc_T; +template class ACE_Malloc; +template class ACE_Allocator_Adapter >; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Malloc_T +#pragma instantiate ACE_Malloc +#pragma instantiate ACE_Allocator_Adapter > +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + diff --git a/ace/Timer/System_Time.h b/ace/Timer/System_Time.h new file mode 100644 index 00000000000..3374d031cac --- /dev/null +++ b/ace/Timer/System_Time.h @@ -0,0 +1,87 @@ +/* -*- C++ -*- */ + + +//============================================================================= +/** + * @file System_Time.h + * + * $Id$ + * + * @author Prashant Jain + * @author Tim H. Harrison and Douglas C. Schmidt + */ +//============================================================================= + + +#ifndef ACE_SYSTEM_TIME_H +#define ACE_SYSTEM_TIME_H +#include "ace/pre.h" + +#include "ace/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Memory_Pool.h" +#include "ace/Malloc_T.h" + +/** + * @class ACE_System_Time + * + * @brief Defines the timer services of the OS interface to access the + * system time either on the local host or on the central time + * server in the network. + */ +class ACE_Export ACE_System_Time +{ +public: + /** + * enumeration types to specify mode of synchronization with master + * clock. Jump will set local system time directly (thus possibly + * producing time gaps or ambiguous local system times. Adjust will + * smoothly slow down or speed up the local system clock to reach + * the system time of the master clock. + */ + enum Sync_Mode { Jump, Adjust }; + + /// Default constructor. + ACE_System_Time (const ACE_TCHAR *poolname = 0); + + /// Default destructor. + ~ACE_System_Time (void); + + /// Get the local system time, i.e., the value returned by + /// . + static int get_local_system_time (ACE_UINT32 &time_out); + + /// Get the local system time, i.e., the value returned by + /// . + static int get_local_system_time (ACE_Time_Value &time_out); + + /// Get the system time of the central time server. + int get_master_system_time (ACE_UINT32 &time_out); + + /// Get the system time of the central time server. + int get_master_system_time (ACE_Time_Value &time_out); + + /// synchronize local system time with the central time server using + /// specified mode. + int sync_local_system_time (ACE_System_Time::Sync_Mode mode); + +private: + typedef ACE_Malloc MALLOC; + typedef ACE_Allocator_Adapter ALLOCATOR; + + /// Our allocator (used for obtaining system time from shared memory). + ALLOCATOR *shmem_; + + /// The name of the pool used by the allocator. + ACE_TCHAR poolname_[MAXPATHLEN + 1]; + + /// Pointer to delta time kept in shared memory. + long *delta_time_; +}; + +#include "ace/post.h" +#endif /* ACE_SYSTEM_TIME_H */ diff --git a/ace/Timer/Time_Request_Reply.cpp b/ace/Timer/Time_Request_Reply.cpp new file mode 100644 index 00000000000..79a17254de2 --- /dev/null +++ b/ace/Timer/Time_Request_Reply.cpp @@ -0,0 +1,188 @@ +// $Id$ + +#include "ace/Time_Request_Reply.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(ace, Time_Request_Reply, "$Id$") + +// Default "do nothing" constructor. + +ACE_Time_Request::ACE_Time_Request (void) +{ + ACE_TRACE ("ACE_Time_Request::ACE_Time_Request"); +} + +// Create a ACE_Time_Request message. + +ACE_Time_Request::ACE_Time_Request (ACE_INT32 t, // Type of request. + const ACE_UINT32 time, + ACE_Time_Value *timeout) // Max time waiting for request. +{ + ACE_TRACE ("ACE_Time_Request::ACE_Time_Request"); + this->msg_type (t); + + // If timeout is a NULL pointer, then block forever... + if (timeout == 0) + { + this->transfer_.block_forever_ = 1; + this->transfer_.sec_timeout_ = 0; + this->transfer_.usec_timeout_ = 0; + } + else // Do a "timed wait." + { + this->block_forever (0); + // Keep track of how long client is willing to wait. + this->transfer_.sec_timeout_ = timeout->sec (); + this->transfer_.usec_timeout_ = timeout->usec (); + } + + // Copy time into request + this->time_ = this->transfer_.time_ = time; +} + +// Initialize length_ in order to avoid problems with byte-ordering +void +ACE_Time_Request::init (void) +{ + ACE_TRACE ("ACE_Time_Request::init"); +// this->length (sizeof this->transfer_); +} + +// Get the fixed size of message +ssize_t +ACE_Time_Request::size (void) const +{ + ACE_TRACE ("ACE_Time_Request::size"); + return sizeof (this->transfer_); +} + +// = Set/get the type of the message. +ACE_INT32 +ACE_Time_Request::msg_type (void) const +{ + ACE_TRACE ("ACE_Time_Request::msg_type"); + return this->transfer_.msg_type_; +} + +void +ACE_Time_Request::msg_type (ACE_INT32 t) +{ + ACE_TRACE ("ACE_Time_Request::msg_type"); + this->transfer_.msg_type_ = t; +} + +// = Set/get the blocking semantics. +ACE_UINT32 +ACE_Time_Request::block_forever (void) const +{ + ACE_TRACE ("ACE_Time_Request::block_forever"); + return this->transfer_.block_forever_; +} + +void +ACE_Time_Request::block_forever (ACE_UINT32 bs) +{ + ACE_TRACE ("ACE_Time_Request::block_forever"); + this->transfer_.block_forever_ = bs; +} + +// = Set/get the timeout. +ACE_Time_Value +ACE_Time_Request::timeout (void) const +{ + ACE_TRACE ("ACE_Time_Request::timeout"); + return ACE_Time_Value (this->transfer_.sec_timeout_, this->transfer_.usec_timeout_); +} + +void +ACE_Time_Request::timeout (const ACE_Time_Value timeout) +{ + ACE_TRACE ("ACE_Time_Request::timeout"); + this->transfer_.sec_timeout_ = timeout.sec (); + this->transfer_.usec_timeout_ = timeout.usec (); +} + +// = Set/get the time +ACE_UINT32 +ACE_Time_Request::time (void) const +{ + ACE_TRACE ("ACE_Time_Request::time"); + return this->time_; +} + +void +ACE_Time_Request::time (ACE_UINT32 t) +{ + ACE_TRACE ("ACE_Time_Request::time"); + this->time_ = t; +} + +// Encode the transfer buffer into network byte order +// so that it can be sent to the server. +int +ACE_Time_Request::encode (void *&buf) +{ + ACE_TRACE ("ACE_Time_Request::encode"); + // Compute the length *before* doing the marshaling. + + buf = (void *) &this->transfer_; + this->transfer_.block_forever_ = htonl (this->transfer_.block_forever_); + this->transfer_.usec_timeout_ = htonl (this->transfer_.usec_timeout_); + this->transfer_.sec_timeout_ = htonl (this->transfer_.sec_timeout_); + this->transfer_.msg_type_ = htonl (this->transfer_.msg_type_); + this->transfer_.time_ = htonl (this->transfer_.time_); + + return this->size (); // Always fixed +} + +// Decode the transfer buffer into host byte byte order +// so that it can be used by the server. +int +ACE_Time_Request::decode (void) +{ + ACE_TRACE ("ACE_Time_Request::decode"); + // Decode + this->transfer_.block_forever_ = ntohl (this->transfer_.block_forever_); + this->transfer_.usec_timeout_ = ntohl (this->transfer_.usec_timeout_); + this->transfer_.sec_timeout_ = ntohl (this->transfer_.sec_timeout_); + this->transfer_.msg_type_ = ntohl (this->transfer_.msg_type_); + this->transfer_.time_ = ntohl (this->transfer_.time_); + + this->time_ = this->transfer_.time_; + return 0; +} + +// Print out the current values of the ACE_Time_Request. + +void +ACE_Time_Request::dump (void) const +{ + ACE_TRACE ("ACE_Time_Request::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("*******\nlength = %d\n"), + this->size ())); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("message-type = "))); + + switch (this->msg_type ()) + { + case ACE_Time_Request::TIME_UPDATE: + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("TIME_UPDATE\n"))); + break; + default: + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" = %d\n"), this->msg_type ())); + break; + } + + if (this->block_forever ()) + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("blocking forever\n"))); + else + { +#if !defined (ACE_NLOGGING) + ACE_Time_Value tv = this->timeout (); +#endif /* ! ACE_NLOGGING */ + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("waiting for %d secs and %d usecs\n"), + tv.sec (), tv.usec ())); + } + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("*******\ntime = %d\n"), + this->time ())); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("+++++++\n"))); +} diff --git a/ace/Timer/Time_Request_Reply.h b/ace/Timer/Time_Request_Reply.h new file mode 100644 index 00000000000..e50f96b0182 --- /dev/null +++ b/ace/Timer/Time_Request_Reply.h @@ -0,0 +1,121 @@ +/* -*- C++ -*- */ +//============================================================================= +/** + * @file Time_Request_Reply.h + * + * $Id$ + * + * Define the format used to exchange messages between the + * ACE_Time_Server and clerks. + * + * @author Prashant Jain + */ +//============================================================================= + + +#ifndef ACE_TIME_REQUEST_REPLY_H +#define ACE_TIME_REQUEST_REPLY_H +#include "ace/pre.h" + +#include "ace/Time_Value.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/SString.h" + +/** + * @class ACE_Time_Request + * + * @brief Message format for delivering requests to the ACE_Time Server. + * + * This class is implemented to minimize data copying. + * In particular, all marshaling is done in situ... + */ +class ACE_Export ACE_Time_Request +{ +public: + enum Constants + { + /// Request message types. + TIME_UPDATE = 01, + + /// Class-specific constant values. + MAX_TIME_LEN = MAXPATHLEN + 1 + }; + + /// Default constructor. + ACE_Time_Request (void); + + /// Create a message. + ACE_Time_Request (ACE_INT32 msg_type, // Type of request. + const ACE_UINT32 time, + ACE_Time_Value *timeout = 0); // Max time waiting for request. + + /// Initialize length_ in order to ensure correct byte ordering + /// before a request is sent. + void init (void); + + // Get the fixed size of message + ssize_t size (void) const; + + // = Set/get the type of the message. + ACE_INT32 msg_type (void) const; + void msg_type (ACE_INT32); + + // = Set/get the time + ACE_UINT32 time (void) const; + void time (ACE_UINT32 t); + + // = Set/get the blocking semantics. + ACE_UINT32 block_forever (void) const; + void block_forever (ACE_UINT32); + + // = Set/get the timeout. + ACE_Time_Value timeout (void) const; + void timeout (const ACE_Time_Value timeout); + + /// Encode the message before transmission. + int encode (void *&); + + /// Decode message after reception. + int decode (void); + + /// Print out the values of the message for debugging purposes. + void dump (void) const; + +private: + // = The 5 fields in the struct are transmitted to the server. + // The remaining 2 fields are not tranferred -- they are used only on + // the server-side to simplify lookups. + + struct Transfer + { + ACE_INT32 msg_type_; + // Type of the request (i.e., ) + + ACE_UINT32 block_forever_; + // Indicates if we should block forever. If 0, then + // and indicates how long we should wait. + + ACE_UINT32 sec_timeout_; + // Max seconds willing to wait for name if not blocking forever. + + ACE_UINT32 usec_timeout_; + // Max micro seconds to wait for name if not blocking forever. + + ACE_UINT32 time_; + // The data portion contains + }; + + /// Transfer buffer. + Transfer transfer_; + + /// Time + ACE_UINT32 time_; +}; + + +#include "ace/post.h" +#endif /* ACE_TIME_REQUEST_REPLY_H */ diff --git a/ace/Timer/Time_Value.h b/ace/Timer/Time_Value.h new file mode 100644 index 00000000000..e9fa59e27f7 --- /dev/null +++ b/ace/Timer/Time_Value.h @@ -0,0 +1,28 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Time_Value.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_TIME_VALUE_H +#define ACE_TIME_VALUE_H +#include "ace/pre.h" + +// This file is no longer used and is only here due to backwards +// compatibility. All the functionality has been merged into OS.h. + +#include "ace/OS/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/post.h" +#endif /* ACE_TIME_VALUE */ diff --git a/ace/Timer/Timeprobe.cpp b/ace/Timer/Timeprobe.cpp new file mode 100644 index 00000000000..cdfdb116913 --- /dev/null +++ b/ace/Timer/Timeprobe.cpp @@ -0,0 +1,44 @@ +// $Id$ + +#include "ace/OS.h" + +ACE_RCSID(ace, Timeprobe, "$Id$") + +#if defined (ACE_COMPILE_TIMEPROBES) + +#include "ace/Timeprobe.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Timeprobe.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Timeprobe; +template class ACE_Function_Timeprobe >; +template class ACE_Unbounded_Set_Iterator; +template class ACE_Unbounded_Set; +template class ACE_Node; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Timeprobe +#pragma instantiate ACE_Function_Timeprobe > +#pragma instantiate ACE_Unbounded_Set_Iterator +#pragma instantiate ACE_Unbounded_Set +#pragma instantiate ACE_Node +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + +# if defined (ACE_TSS_TIMEPROBES) +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_TSS_Singleton; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_TSS_Singleton +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ +# else /* ACE_TSS_TIMEPROBES */ +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Singleton; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Singleton +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ +# endif /* ACE_TSS_TIMEPROBES */ + +#endif /* ACE_COMPILE_TIMEPROBES */ + diff --git a/ace/Timer/Timeprobe.h b/ace/Timer/Timeprobe.h new file mode 100644 index 00000000000..91033b54ece --- /dev/null +++ b/ace/Timer/Timeprobe.h @@ -0,0 +1,176 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timeprobe.h + * + * $Id$ + * + * @author Irfan Pyarali + * + * If users want to use time probes the ACE_COMPILE_TIMEPROBES flag + * must be defined when compiling ACE. This can be achieved by doing + * one of the following: . Use make probe = 1 if you are using the + * make utility. . Define ACE_COMPILE_TIMEPROBES in config.h . Define + * ACE_COMPILE_TIMEPROBES in the VC project file. . Other regular + * methods will also work. It is not necessary to define + * ACE_COMPILE_TIMEPROBES when using time probes you simply need + * ACE_ENABLE_TIMEPROBES. You can use the ACE_TIMEPROBE_* macros to + * program the time probes and use the ACE_ENABLE_TIMEPROBE to enable + * the time probes. If you define ACE_ENABLE_TIMEPROBE in your code + * but forget to compile ACE with ACE_COMPILE_TIMEPROBES you will end + * up with linker errors. Remember that ACE_COMPILE_TIMEPROBES means + * that the ACE library will contain code for time probes. This is + * only useful when compiling ACE. ACE_ENABLE_TIMEPROBES means that + * the ACE_TIMEPROBE_* macros should spring to life. + * + */ +//============================================================================= + + +#ifndef ACE_TIMEPROBE_H +#define ACE_TIMEPROBE_H +#include "ace/pre.h" + +#include "ace/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_ENABLE_TIMEPROBES) + #if !defined (ACE_COMPILE_TIMEPROBES) + #define ACE_COMPILE_TIMEPROBES + #endif /* ACE_COMPILE_TIMEPROBES */ +#endif /* ACE_ENABLE_TIMEPROBES */ + +#if defined (ACE_COMPILE_TIMEPROBES) + +/** + * @class ACE_Event_Descriptions + * + * @brief Event Descriptions. + */ +class ACE_Export ACE_Event_Descriptions +{ +public: + /// Event descriptions + const char **descriptions_; + + /// Minimum id of this description set + u_long minimum_id_; + + /// Comparison + int operator== (const ACE_Event_Descriptions &rhs) const; +}; + +/** + * @class ACE_timeprobe_t + * + * @brief Time probe record. + */ +class ACE_Export ACE_timeprobe_t +{ +public: + /// Events are record as strings or numbers. + union event + { + u_long event_number_; + const char *event_description_; + }; + + /// Type of event. + enum event_type + { + NUMBER, + STRING + }; + + /// Event. + event event_; + + /// Type of event. + event_type event_type_; + + /// Timestamp. + ACE_hrtime_t time_; + + /// Id of thread posting the time probe. + ACE_thread_t thread_; +}; + +#if defined (__ACE_INLINE__) +#include "ace/Timeprobe.i" +#endif /* __ACE_INLINE__ */ + +#include "ace/Synch.h" +#include "ace/Singleton.h" +#include "ace/Timeprobe_T.h" + +// If ACE_MT_TIMEPROBES is defined, use a Thread_Mutex to lock the +// internal state of ACE_Timerprobe. This allows multiple threads to +// use the same ACE_Timerprobe. +# if defined (ACE_MT_TIMEPROBES) +typedef ACE_SYNCH_MUTEX ACE_TIMEPROBE_MUTEX; +# else /* ACE_MT_TIMEPROBES */ +typedef ACE_SYNCH_NULL_MUTEX ACE_TIMEPROBE_MUTEX; +# endif /* ACE_MT_TIMEPROBES */ + +typedef ACE_Timeprobe + ACE_TIMEPROBE_WITH_LOCKING; + +// If ACE_TSS_TIMEPROBES is defined, store the ACE_Timeprobe singleton +// in thread specific storage. This allows multiple threads to use +// their own instance of ACE_Timerprobe, without interfering with each +// other. +# if defined (ACE_TSS_TIMEPROBES) +# define ACE_TIMEPROBE_SINGLETON_TYPE ACE_TSS_Singleton +# define ACE_TIMEPROBE_SINGLETON_LOCK_TYPE ACE_SYNCH_NULL_MUTEX +# else /* ACE_TSS_TIMEPROBES */ +# define ACE_TIMEPROBE_SINGLETON_TYPE ACE_Singleton +# define ACE_TIMEPROBE_SINGLETON_LOCK_TYPE ACE_SYNCH_MUTEX +# endif /* ACE_TSS_TIMEPROBES */ + +#if defined (_MSC_VER) +// Disable warning of using Microsoft Extension. +#pragma warning(disable:4231) +#endif /* _MSC_VER */ + +typedef ACE_TIMEPROBE_SINGLETON_TYPE + ACE_TIMEPROBE_SINGLETON; + +ACE_SINGLETON_DECLARE (ACE_TIMEPROBE_SINGLETON_TYPE, \ + ACE_TIMEPROBE_WITH_LOCKING, \ + ACE_TIMEPROBE_SINGLETON_LOCK_TYPE); + +#if defined (_MSC_VER) +// Default back the warning of using Microsoft Extension. +#pragma warning(default:4231) +#endif /* _MSC_VER */ + +#endif /* ACE_COMPILE_TIMEPROBES */ + +// If ACE_ENABLE_TIMEPROBES is defined, the macros below will +// work. Otherwise, they just vanish. Using this macro, you can +// control which files/libraries are probed. +#if defined (ACE_ENABLE_TIMEPROBES) && defined (ACE_COMPILE_TIMEPROBES) + +# define ACE_TIMEPROBE_RESET ACE_TIMEPROBE_SINGLETON::instance ()->reset () +# define ACE_TIMEPROBE(id) ACE_TIMEPROBE_SINGLETON::instance ()->timeprobe (id) +# define ACE_TIMEPROBE_PRINT ACE_TIMEPROBE_SINGLETON::instance ()->print_times () +# define ACE_TIMEPROBE_PRINT_ABSOLUTE ACE_TIMEPROBE_SINGLETON::instance ()->print_absolute_times () +# define ACE_TIMEPROBE_EVENT_DESCRIPTIONS(descriptions, minimum_id) static int ace_timeprobe_##descriptions##_return = ACE_TIMEPROBE_SINGLETON::instance ()->event_descriptions (descriptions, minimum_id) +# define ACE_FUNCTION_TIMEPROBE(X) ACE_Function_Timeprobe function_timeprobe (*ACE_TIMEPROBE_SINGLETON::instance (), X) + +#else /* ACE_ENABLE_TIMEPROBES && ACE_COMPILE_TIMEPROBES */ + +# define ACE_TIMEPROBE_RESET +# define ACE_TIMEPROBE(id) +# define ACE_TIMEPROBE_PRINT +# define ACE_TIMEPROBE_PRINT_ABSOLUTE +# define ACE_TIMEPROBE_EVENT_DESCRIPTIONS(descriptions, minimum_id) +# define ACE_FUNCTION_TIMEPROBE(X) + +#endif /* ACE_ENABLE_TIMEPROBES && ACE_COMPILE_TIMEPROBES */ +#include "ace/post.h" +#endif /* ACE_TIMEPROBE_H */ diff --git a/ace/Timer/Timeprobe.i b/ace/Timer/Timeprobe.i new file mode 100644 index 00000000000..31f022f77b4 --- /dev/null +++ b/ace/Timer/Timeprobe.i @@ -0,0 +1,8 @@ +// $Id$ + +ACE_INLINE int +ACE_Event_Descriptions::operator== (const ACE_Event_Descriptions &rhs) const +{ + return this->minimum_id_ == rhs.minimum_id_ && + this->descriptions_ == rhs.descriptions_; +} diff --git a/ace/Timer/Timeprobe_T.cpp b/ace/Timer/Timeprobe_T.cpp new file mode 100644 index 00000000000..988565ccee1 --- /dev/null +++ b/ace/Timer/Timeprobe_T.cpp @@ -0,0 +1,300 @@ +// $Id$ + +#ifndef ACE_TIMEPROBE_T_C +#define ACE_TIMEPROBE_T_C + +#include "ace/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +ACE_RCSID(ace, Timeprobe_T, "$Id$") + +#if defined (ACE_COMPILE_TIMEPROBES) + +#include "ace/Timeprobe.h" +#include "ace/High_Res_Timer.h" + +template +ACE_Timeprobe::ACE_Timeprobe (u_long size) + : timeprobes_ (0), + lock_ (), + max_size_ (size), + current_size_ (0) +{ + ACE_NEW (this->timeprobes_, + ACE_timeprobe_t[this->max_size_]); + +#if defined (VXWORKS) + if (sysProcNumGet () == 0) + this->current_slot_vme_address_ = (u_int *) 0xDa010000; + else + this->current_slot_vme_address_ = (u_int *) 0xD8010000; +#endif /* VXWORKS */ +} + +template +ACE_Timeprobe::ACE_Timeprobe (const ACE_Timeprobe &) +{ + // + // Stupid MSVC is forcing me to define this; please don't use it. + // + + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("ACE_NOTSUP: %s, line %d\n"), __FILE__, __LINE__)); + errno = ENOTSUP; +} + +template +ACE_Timeprobe::~ACE_Timeprobe (void) +{ + delete [] this->timeprobes_; +} + +template void +ACE_Timeprobe::timeprobe (u_long event) +{ + ACE_GUARD (ACE_LOCK, ace_mon, this->lock_); + + ACE_ASSERT (this->current_size_ < this->max_size_); + + this->timeprobes_[this->current_size_].event_.event_number_ = event; + this->timeprobes_[this->current_size_].event_type_ = ACE_timeprobe_t::NUMBER; + this->timeprobes_[this->current_size_].time_ = ACE_OS::gethrtime (); + this->timeprobes_[this->current_size_].thread_ = ACE_OS::thr_self (); + + this->current_size_++; + +#if defined (VMETRO_TIME_TEST) && (VXWORKS) + // If we are using the VMETRO board to get time samples, then write + // to the other boards VME address. + *this->current_slot_vme_address_ = event; +#endif /* VMETRO_TIME_TEST && VXWORKS */ +} + +template void +ACE_Timeprobe::timeprobe (const char *event) +{ + ACE_GUARD (ACE_LOCK, ace_mon, this->lock_); + + ACE_ASSERT (this->current_size_ < this->max_size_); + + this->timeprobes_[this->current_size_].event_.event_description_ = event; + this->timeprobes_[this->current_size_].event_type_ = ACE_timeprobe_t::STRING; + this->timeprobes_[this->current_size_].time_ = ACE_OS::gethrtime (); + this->timeprobes_[this->current_size_].thread_ = ACE_OS::thr_self (); + + this->current_size_++; +} + +template void +ACE_Timeprobe::reset (void) +{ + ACE_GUARD (ACE_LOCK, ace_mon, this->lock_); + + this->current_size_ = 0; +} + +template ACE_Unbounded_Set & +ACE_Timeprobe::event_descriptions (void) +{ + return this->event_descriptions_; +} + +template ACE_Unbounded_Set & +ACE_Timeprobe::sorted_event_descriptions (void) +{ + return this->sorted_event_descriptions_; +} + +template u_int * +ACE_Timeprobe::current_slot_vme_address (void) +{ + return this->current_slot_vme_address_; +} + +template ACE_timeprobe_t * +ACE_Timeprobe::timeprobes (void) +{ + return this->timeprobes_; +} + +template ACE_LOCK & +ACE_Timeprobe::lock (void) +{ + return this->lock_; +} + +template u_long +ACE_Timeprobe::max_size (void) +{ + return this->max_size_; +} + +template u_long +ACE_Timeprobe::current_size (void) +{ + return this->current_size_; +} + +template int +ACE_Timeprobe::event_descriptions (const char **descriptions, + u_long minimum_id) +{ + ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->lock_, -1); + + ACE_Event_Descriptions events; + events.descriptions_ = descriptions; + events.minimum_id_ = minimum_id; + + this->event_descriptions_.insert (events); + + return 0; +} + +template void +ACE_Timeprobe::print_times (void) +{ + ACE_GUARD (ACE_LOCK, ace_mon, this->lock_); + + // Sort the event descriptions + this->sort_event_descriptions_i (); + + ACE_DEBUG ((LM_DEBUG, + "\nACE_Timeprobe; %d timestamps were recorded:\n", + this->current_size_)); + + if (this->current_size_ == 0) + return; + + ACE_DEBUG ((LM_DEBUG, + "\n%-50.50s %8.8s %13.13s\n\n", + "Event", + "thread", + "usec")); + + ACE_DEBUG ((LM_DEBUG, + "%-50.50s %8.8x %13.13s\n", + this->find_description_i (0), + this->timeprobes_[0].thread_, + "START")); + + ACE_UINT32 gsf = ACE_High_Res_Timer::global_scale_factor (); + for (u_long i = 1; i < this->current_size_; i++) + { + ACE_hrtime_t time_difference = + this->timeprobes_[i].time_ - this->timeprobes_[i-1].time_; + + ACE_UINT32 elapsed_time_in_micro_seconds = + (ACE_UINT32) (time_difference / gsf); + ACE_UINT32 remainder = + (ACE_UINT32) (time_difference % gsf); + // Convert to the fractional part in microseconds, with 3 digits + // of precision (hence the 1000). + ACE_UINT32 fractional = remainder * 1000 / gsf; + + ACE_DEBUG ((LM_DEBUG, + "%-50.50s %8.8x %10u.%03.3u\n", + this->find_description_i (i), + this->timeprobes_[i].thread_, + (unsigned int) elapsed_time_in_micro_seconds, + (unsigned int) fractional)); + } +} + +template void +ACE_Timeprobe::print_absolute_times (void) +{ + ACE_GUARD (ACE_LOCK, ace_mon, this->lock_); + + // Sort the event descriptions + this->sort_event_descriptions_i (); + + ACE_DEBUG ((LM_DEBUG, + "\nACE_Timeprobe; %d timestamps were recorded:\n", + this->current_size_)); + + if (this->current_size_ == 0) + return; + + ACE_DEBUG ((LM_DEBUG, + "\n%-50.50s %8.8s %13.13s\n\n", + "Event", + "thread", + "stamp")); + + for (u_long i = 0; i < this->current_size_; i++) + { + char buf[64]; + ACE_OS::sprintf (buf, "%llu", this->timeprobes_[i].time_); + ACE_DEBUG ((LM_DEBUG, + "%-50.50s %8.8x %13.13s\n", + this->find_description_i (i), + this->timeprobes_[i].thread_, + buf)); + } +} + +template const char * +ACE_Timeprobe::find_description_i (u_long i) +{ + if (this->timeprobes_[i].event_type_ == ACE_timeprobe_t::STRING) + return this->timeprobes_[i].event_.event_description_; + else + { + EVENT_DESCRIPTIONS::iterator iterator = this->sorted_event_descriptions_.begin (); + for (u_long j = 0; + j < this->sorted_event_descriptions_.size () - 1; + iterator++, j++) + { + EVENT_DESCRIPTIONS::iterator next_event_descriptions = iterator; + next_event_descriptions++; + + if (this->timeprobes_[i].event_.event_number_ < (*next_event_descriptions).minimum_id_) + break; + } + return (*iterator).descriptions_[this->timeprobes_[i].event_.event_number_ - (*iterator).minimum_id_]; + } +} + +template void +ACE_Timeprobe::sort_event_descriptions_i (void) +{ + size_t total_elements = this->event_descriptions_.size (); + + for (size_t i = 0; + i < total_elements; + i++) + { + EVENT_DESCRIPTIONS::iterator iterator = this->event_descriptions_.begin (); + ACE_Event_Descriptions min_entry = *iterator; + + for (; + iterator != this->event_descriptions_.end (); + iterator++) + if ((*iterator).minimum_id_ < min_entry.minimum_id_) + min_entry = *iterator; + + this->sorted_event_descriptions_.insert (min_entry); + this->event_descriptions_.remove (min_entry); + } +} + +template +ACE_Function_Timeprobe::ACE_Function_Timeprobe (Timeprobe &timeprobe, + u_long event) + : timeprobe_ (timeprobe), + event_ (event) +{ + this->timeprobe_.timeprobe (this->event_); +} + +template +ACE_Function_Timeprobe::~ACE_Function_Timeprobe (void) +{ + this->timeprobe_.timeprobe (this->event_ + 1); +} + +#endif /* ACE_COMPILE_TIMEPROBES */ +#endif /* ACE_TIMEPROBE_T_C */ diff --git a/ace/Timer/Timeprobe_T.h b/ace/Timer/Timeprobe_T.h new file mode 100644 index 00000000000..52740f6456f --- /dev/null +++ b/ace/Timer/Timeprobe_T.h @@ -0,0 +1,188 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timeprobe_T.h + * + * $Id$ + * + * @author Irfan Pyarali + */ +//============================================================================= + + +#ifndef ACE_TIMEPROBE_T_H +#define ACE_TIMEPROBE_T_H +#include "ace/pre.h" + +#include "ace/OS.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_COMPILE_TIMEPROBES) + +#include "ace/Unbounded_Set.h" + +/** + * @class ACE_Timeprobe + * + * @brief This class is used to instrument code. This is accomplished + * by inserting time probes at different location in the code. + * ACE_Timeprobe then measures the time difference between two + * time probes. + * + * This class provides a lightweight implementation for + * measuring the time required to execute code between two time + * probes. When a time probe executes, it records the time, the + * id of the calling thread, and an event description. The + * event description can either be an unsigned long or a string + * (char *). If string are used, care must be taken cause only + * pointer copies are done and the string data is *not* copied. + * The recorded time probes can then be printed by calling + * . If you have used unsigned longs as event + * descriptions in any of your time probes, you must have + * provided an event description table that maps the unsigned + * longs to readable strings. This map is a simple array of + * strings, and the event number is used as the index into the + * array when looking for the event description. If you have + * only used strings for the event description, this map is not + * necessary. + * Multiple maps can also be used to chunk up the time probes. + * Each one can be added by calling . + * Different tables are used internally by consulting the + * minimum_id for each table. It is up to the user to make sure + * that multiple tables do not share the same event id range. + */ +template +class ACE_Timeprobe +{ +public: + + /// Self + typedef ACE_Timeprobe + SELF; + + /// We can hold multiple event description tables. + typedef ACE_Unbounded_Set + EVENT_DESCRIPTIONS; + + /// Create Timeprobes with slots + ACE_Timeprobe (u_long size = ACE_DEFAULT_TIMEPROBE_TABLE_SIZE); + + /// Destructor. + ~ACE_Timeprobe (void); + + /// Record a time. is used to describe this time probe. + void timeprobe (u_long event); + + /// Record a time. is used to describe this time probe. + void timeprobe (const char *id); + + /// Record event descriptions. + int event_descriptions (const char **descriptions, + u_long minimum_id); + + /// Print the time probes. + void print_times (void); + + /// Print the time probes. + void print_absolute_times (void); + + /// Reset the slots. All old time probes will be lost. + void reset (void); + + ACE_Timeprobe (const ACE_Timeprobe &); + // Not implemented (stupid MSVC won't let it be protected). + + // = (Somewhat private) Accessors + + /// Event Descriptions + ACE_Unbounded_Set &event_descriptions (void); + + /// Sorted Event Descriptions. + ACE_Unbounded_Set &sorted_event_descriptions (void); + + /// VME slot address. + u_int *current_slot_vme_address (void); + + /// Find description of event + const char *find_description_i (u_long i); + + /// Sort event descriptions + void sort_event_descriptions_i (void); + + /// Time probe slots + ACE_timeprobe_t *timeprobes (void); + + /// Synchronization variable. + ACE_LOCK &lock (void); + + /// Max size of timestamp table + u_long max_size (void); + + /// Current size of timestamp table + u_long current_size (void); + +protected: + + /// Event Descriptions + EVENT_DESCRIPTIONS event_descriptions_; + + /// Sorted Event Descriptions. + EVENT_DESCRIPTIONS sorted_event_descriptions_; + + /// Added sections below here to make compatible with the VMETRO + /// board test. + u_int *current_slot_vme_address_; + + /// Time probe slots + ACE_timeprobe_t *timeprobes_; + + /// Synchronization variable. + ACE_LOCK lock_; + + /// Max size of timestamp table + u_long max_size_; + + /// Current size of timestamp table + u_long current_size_; +}; + +/** + * @class ACE_Function_Timeprobe + * + * @brief Auto pointer like time probes. It will record on + * construction and on destruction. + */ +template +class ACE_Function_Timeprobe +{ +public: + /// Constructor. + ACE_Function_Timeprobe (Timeprobe &timeprobe, + u_long event); + + /// Destructor. + ~ACE_Function_Timeprobe (void); + +protected: + /// Reference to timeprobe. + Timeprobe &timeprobe_; + + /// Event. + u_long event_; +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "ace/Timeprobe_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Timeprobe_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#endif /* ACE_COMPILE_TIMEPROBES */ +#include "ace/post.h" +#endif /* ACE_TIMEPROBE_T_H */ diff --git a/ace/Timer/Timer_Hash.cpp b/ace/Timer/Timer_Hash.cpp new file mode 100644 index 00000000000..a37df2e2fb4 --- /dev/null +++ b/ace/Timer/Timer_Hash.cpp @@ -0,0 +1,123 @@ +// $Id$ + +// Timer_Hash.cpp + +#if !defined (ACE_TIMER_HASH_C) +#define ACE_TIMER_HASH_C + +#include "ace/Timer_Hash.h" + +#if defined (ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_Hash_T.cpp" +#endif /* ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +ACE_RCSID(ace, Timer_Hash, "$Id$") + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Free_List >; +template class ACE_Locked_Free_List, + ACE_Null_Mutex>; +template class ACE_Timer_Hash_Upcall , + ACE_SYNCH_RECURSIVE_MUTEX>; + +template class ACE_Timer_Queue_T ; + +template class ACE_Timer_Queue_Iterator_T ; + +template class ACE_Timer_List_T ; + +template class ACE_Timer_List_Iterator_T ; + +template class ACE_Timer_Heap_T ; + +template class ACE_Timer_Heap_Iterator_T ; + +template class ACE_Timer_Hash_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_List>; + +template class ACE_Timer_Hash_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_List>; + +template class ACE_Timer_Hash_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_Heap>; + +template class ACE_Timer_Hash_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_Heap>; + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Free_List > +#pragma instantiate ACE_Locked_Free_List, \ + ACE_Null_Mutex> +#pragma instantiate ACE_Timer_Hash_Upcall , \ + ACE_SYNCH_RECURSIVE_MUTEX> + +#pragma instantiate ACE_Timer_Queue_T + +#pragma instantiate ACE_Timer_Queue_Iterator_T + +#pragma instantiate ACE_Timer_List_T + +#pragma instantiate ACE_Timer_List_Iterator_T + +#pragma instantiate ACE_Timer_Heap_T + +#pragma instantiate ACE_Timer_Heap_Iterator_T + +#pragma instantiate ACE_Timer_Hash_T, \ + ACE_SYNCH_RECURSIVE_MUTEX, \ + ACE_Hash_Timer_List> + +#pragma instantiate ACE_Timer_Hash_Iterator_T, \ + ACE_SYNCH_RECURSIVE_MUTEX, \ + ACE_Hash_Timer_List> + +#pragma instantiate ACE_Timer_Hash_T, \ + ACE_SYNCH_RECURSIVE_MUTEX, \ + ACE_Hash_Timer_Heap> + +#pragma instantiate ACE_Timer_Hash_Iterator_T, \ + ACE_SYNCH_RECURSIVE_MUTEX, \ + ACE_Hash_Timer_Heap> + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + + +#endif /* ACE_TIMER_HASH_C */ diff --git a/ace/Timer/Timer_Hash.h b/ace/Timer/Timer_Hash.h new file mode 100644 index 00000000000..189fbb7bace --- /dev/null +++ b/ace/Timer/Timer_Hash.h @@ -0,0 +1,71 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_Hash.h + * + * $Id$ + * + * @author Darrell Brunsch + */ +//============================================================================= + + +#ifndef ACE_TIMER_HASH_H +#define ACE_TIMER_HASH_H +#include "ace/pre.h" + +#include "ace/Timer_Hash_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Timer_Heap_T.h" +#include "ace/Timer_List_T.h" + +// The following typedef are here for ease of use + +typedef ACE_Timer_Hash_Upcall , + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Hash_Upcall; + +typedef ACE_Timer_List_T + ACE_Hash_Timer_List; + +typedef ACE_Timer_Heap_T + ACE_Hash_Timer_Heap; + + +typedef ACE_Timer_Hash_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_List> + + ACE_Timer_Hash; + +typedef ACE_Timer_Hash_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_List> + ACE_Timer_Hash_Iterator; + +typedef ACE_Timer_Hash_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_Heap> + ACE_Timer_Hash_Heap; + +typedef ACE_Timer_Hash_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX, + ACE_Hash_Timer_Heap> + ACE_Timer_Hash_Heap_Iterator; + +#include "ace/post.h" +#endif /* ACE_TIMER_HASH_H */ diff --git a/ace/Timer/Timer_Hash_T.cpp b/ace/Timer/Timer_Hash_T.cpp new file mode 100644 index 00000000000..ac303cca469 --- /dev/null +++ b/ace/Timer/Timer_Hash_T.cpp @@ -0,0 +1,622 @@ +// $Id$ + +#ifndef ACE_TIMER_HASH_T_C +#define ACE_TIMER_HASH_T_C + +#include "ace/Timer_Hash_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/High_Res_Timer.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(ace, + Timer_Hash_T, + "$Id$") + +struct Hash_Token +{ + Hash_Token (const void *act, + size_t pos, + long orig_id) + : act_ (act), + pos_ (pos), + orig_id_ (orig_id) + {} + + const void *act_; + size_t pos_; + long orig_id_; +}; + +// Default constructor + +template +ACE_Timer_Hash_Upcall::ACE_Timer_Hash_Upcall (void) + : timer_hash_ (0) +{ + // Nothing +} + +// Constructor that specifies a Timer_Hash to call up to + +template +ACE_Timer_Hash_Upcall::ACE_Timer_Hash_Upcall (ACE_Timer_Queue_T *timer_hash) + : timer_hash_ (timer_hash) +{ + // Nothing +} + +// Calls up to timer_hash's upcall functor + +template int +ACE_Timer_Hash_Upcall::timeout (ACE_Timer_Queue_T, + ACE_Null_Mutex> &timer_queue, + ACE_Event_Handler *handler, + const void *arg, + const ACE_Time_Value &cur_time) +{ + ACE_UNUSED_ARG (timer_queue); + + Hash_Token *h = ACE_reinterpret_cast (Hash_Token *, + ACE_const_cast (void *, + arg)); + int result = + this->timer_hash_->upcall_functor ().timeout (*this->timer_hash_, + handler, + h->act_, + cur_time); + delete h; + return result; +} + + +// Calls up to timer_hash's upcall functor + +template int +ACE_Timer_Hash_Upcall::cancellation (ACE_Timer_Queue_T, + ACE_Null_Mutex> &timer_queue, + ACE_Event_Handler *handler) +{ + ACE_UNUSED_ARG (timer_queue); + return this->timer_hash_->upcall_functor ().cancellation (*this->timer_hash_, + handler); +} + + +// Calls up to timer_hash's upcall functor + +template int +ACE_Timer_Hash_Upcall::deletion (ACE_Timer_Queue_T, + ACE_Null_Mutex> &timer_queue, + ACE_Event_Handler *handler, + const void *arg) +{ + ACE_UNUSED_ARG (timer_queue); + + Hash_Token *h = ACE_reinterpret_cast (Hash_Token *, + ACE_const_cast (void *, + arg)); + int result = + this->timer_hash_->upcall_functor ().deletion (*this->timer_hash_, + handler, + h->act_); + delete h; + return result; +} + + + +template +ACE_Timer_Hash_Iterator_T::ACE_Timer_Hash_Iterator_T (ACE_Timer_Hash_T &hash) + : timer_hash_ (hash) +{ + this->first (); + // Nothing +} + +// Positions the iterator at the first node in the timing hash table + +template void +ACE_Timer_Hash_Iterator_T::first (void) +{ + for (this->position_ = 0; + this->position_ < this->timer_hash_.table_size_; + this->position_++) + { + // Check for an empty entry + if (!this->timer_hash_.table_[this->position_]->is_empty ()) + { + this->iter_ = &this->timer_hash_.table_[this->position_]->iter (); + this->iter_->first (); + return; + } + } + + // Didn't find any + this->iter_ = 0; +} + +// Positions the iterator at the next node in the bucket or goes to the next +// bucket + +template void +ACE_Timer_Hash_Iterator_T::next (void) +{ + if (this->isdone ()) + return; + + // If there is no more in the current bucket, go to the next + if (this->iter_->isdone ()) + { + for (this->position_++; + this->position_ < this->timer_hash_.table_size_; + this->position_++) + { + // Check for an empty entry + if (!this->timer_hash_.table_[this->position_]->is_empty ()) + { + this->iter_ = &this->timer_hash_.table_[this->position_]->iter (); + this->iter_->first (); + return; + } + } + + // Didn't find any. + this->iter_ = 0; + } + else + this->iter_->next (); +} + +// Returns true when we are at the end (when bucket_item_ == 0) + +template int +ACE_Timer_Hash_Iterator_T::isdone (void) const +{ + return this->iter_ == 0; +} + +// Returns the node at the current position in the sequence + +template ACE_Timer_Node_T * +ACE_Timer_Hash_Iterator_T::item (void) +{ + if (this->isdone ()) + return 0; + + return this->iter_->item (); +} + +template ACE_Timer_Queue_Iterator_T & +ACE_Timer_Hash_T::iter (void) +{ + this->iterator_->first (); + return *this->iterator_; +} + +// Create an empty queue. + +template +ACE_Timer_Hash_T::ACE_Timer_Hash_T (size_t table_size, + FUNCTOR *upcall_functor, + ACE_Free_List > *freelist) + : ACE_Timer_Queue_T (upcall_functor, freelist), + size_ (0), + table_size_ (table_size), + table_functor_ (this), + earliest_position_ (0) +{ + ACE_TRACE ("ACE_Timer_Hash_T::ACE_Timer_Hash_T"); + + ACE_NEW (table_, + BUCKET *[table_size]); + + this->gettimeofday (ACE_OS::gettimeofday); + + for (size_t i = 0; + i < table_size; + i++) + { + ACE_NEW (this->table_[i], + BUCKET (&this->table_functor_, + this->free_list_)); + this->table_[i]->gettimeofday (ACE_OS::gettimeofday); + } + + ACE_NEW (iterator_, + HASH_ITERATOR (*this)); +} + + +template +ACE_Timer_Hash_T::ACE_Timer_Hash_T (FUNCTOR *upcall_functor, + ACE_Free_List > *freelist) + : ACE_Timer_Queue_T (upcall_functor, freelist), + size_ (0), + table_size_ (ACE_DEFAULT_TIMER_HASH_TABLE_SIZE), + table_functor_ (this), + earliest_position_ (0) +{ + ACE_TRACE ("ACE_Timer_Hash_T::ACE_Timer_Hash_T"); + + ACE_NEW (table_, + BUCKET *[ACE_DEFAULT_TIMER_HASH_TABLE_SIZE]); + + + this->gettimeofday (ACE_OS::gettimeofday); + + for (size_t i = 0; + i < this->table_size_; + i++) + { + ACE_NEW (this->table_[i], + BUCKET (&this->table_functor_, + this->free_list_)); + this->table_[i]->gettimeofday (ACE_OS::gettimeofday); + } + + ACE_NEW (iterator_, + HASH_ITERATOR (*this)); +} + +// Remove all remaining items in the Queue. + +template +ACE_Timer_Hash_T::~ACE_Timer_Hash_T (void) +{ + ACE_TRACE ("ACE_Timer_Hash_T::~ACE_Timer_Hash_T"); + ACE_MT (ACE_GUARD (ACE_LOCK, ace_mon, this->mutex_)); + + delete iterator_; + + for (size_t i = 0; + i < this->table_size_; + i++) + delete this->table_[i]; + + delete [] this->table_; +} + +// Checks if queue is empty. + +template int +ACE_Timer_Hash_T::is_empty (void) const +{ + ACE_TRACE ("ACE_Timer_Hash_T::is_empty"); + return this->table_[this->earliest_position_]->is_empty (); +} + +// Returns earliest time in a non-empty bucket + +template const ACE_Time_Value & +ACE_Timer_Hash_T::earliest_time (void) const +{ + ACE_TRACE ("ACE_Timer_Hash_T::earliest_time"); + return this->table_[this->earliest_position_]->earliest_time (); +} + +template void +ACE_Timer_Hash_T::dump (void) const +{ + ACE_TRACE ("ACE_Timer_Hash_T::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ntable_size_ = %d"), this->table_size_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nearliest_position_ = %d"), this->earliest_position_)); + + for (size_t i = 0; i < this->table_size_; i++) + if (!this->table_[i]->is_empty ()) + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nBucket %d contains nodes"), i)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +// Reschedule a periodic timer. This function must be called with the +// mutex lock held. + +template void +ACE_Timer_Hash_T::reschedule (ACE_Timer_Node_T *expired) +{ + ACE_TRACE ("ACE_Timer_Hash_T::reschedule"); + + size_t position = + expired->get_timer_value ().usec () % this->table_size_; + + Hash_Token *h = ACE_reinterpret_cast (Hash_Token *, + ACE_const_cast (void *, + expired->get_act ())); + + h->orig_id_ = this->table_[position]->schedule (expired->get_type (), + h, + expired->get_timer_value (), + expired->get_interval ()); + + if (this->table_[this->earliest_position_]->is_empty () + || this->table_[position]->earliest_time () + < this->table_[this->earliest_position_]->earliest_time ()) + this->earliest_position_ = position; +} + +// Insert a new handler that expires at time future_time; if interval +// is > 0, the handler will be reinvoked periodically. + +template long +ACE_Timer_Hash_T::schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &future_time, + const ACE_Time_Value &interval) +{ + ACE_TRACE ("ACE_Timer_Hash_T::schedule"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + size_t position = + future_time.usec () % this->table_size_; + + Hash_Token *h; + + ACE_NEW_RETURN (h, + Hash_Token (act, + position, + 0), + -1); + + h->orig_id_ = this->table_[position]->schedule (type, + h, + future_time, + interval); + + if (this->table_[this->earliest_position_]->is_empty () + || this->table_[position]->earliest_time () + < this->table_[this->earliest_position_]->earliest_time ()) + this->earliest_position_ = position; + + ++this->size_; + + return ACE_reinterpret_cast (long, + h); +} + +// Locate and update the inteval on the timer_id + +template int +ACE_Timer_Hash_T::reset_interval (long timer_id, + const ACE_Time_Value &interval) +{ + ACE_TRACE ("ACE_Timer_Hash_T::reset_interval"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Make sure we are getting a valid , not an error + // returned by . + if (timer_id == -1) + return -1; + + Hash_Token *h = ACE_reinterpret_cast (Hash_Token *, + timer_id); + + return this->table_[h->pos_]->reset_interval (h->orig_id_, + interval); +} + +// Locate and remove the single with a value of +// from the correct table timer queue. + +template int +ACE_Timer_Hash_T::cancel (long timer_id, + const void **act, + int dont_call) +{ + ACE_TRACE ("ACE_Timer_Hash_T::cancel"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Make sure we are getting a valid , not an error + // returned by . + if (timer_id == -1) + return 0; + + Hash_Token *h = ACE_reinterpret_cast (Hash_Token *, + timer_id); + + int result = this->table_[h->pos_]->cancel (h->orig_id_, + act, + dont_call); + + if (h->pos_ == this->earliest_position_) + this->find_new_earliest (); + + if (act != 0) + *act = h->act_; + + delete h; + + --this->size_; + + return result; +} + +// Locate and remove all values of from the timer queue. + +template int +ACE_Timer_Hash_T::cancel (const TYPE &type, + int dont_call) +{ + ACE_TRACE ("ACE_Timer_Hash_T::cancel"); + + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + size_t i; // loop variable. + + Hash_Token **timer_ids; + + ACE_NEW_RETURN (timer_ids, + Hash_Token *[this->size_], + -1); + size_t pos = 0; + + for (i = 0; + i < this->table_size_; + i++) + { + ACE_Timer_Queue_Iterator_T, + ACE_Null_Mutex> &iter = + this->table_[i]->iter (); + + for (iter.first (); + !iter.isdone (); + iter.next ()) + if (iter.item ()->get_type () == type) + timer_ids[pos++] = + ACE_reinterpret_cast (Hash_Token *, + ACE_const_cast (void *, + iter.item ()->get_act ())); + } + + if (pos > this->size_) + return -1; + + for (i = 0; i < pos; i++) + { + this->table_[timer_ids[i]->pos_]->cancel (timer_ids[i]->orig_id_, + 0, + 1); + delete timer_ids[i]; + --this->size_; + } + + delete [] timer_ids; + + if (dont_call == 0) + this->upcall_functor ().cancellation (*this, + type); + this->find_new_earliest (); + + return pos; +} + +// Removes the earliest node and finds the new earliest position + +template ACE_Timer_Node_T * +ACE_Timer_Hash_T::remove_first (void) +{ + if (this->is_empty ()) + return 0; + + ACE_Timer_Node_T *temp = + this->table_[this->earliest_position_]->remove_first (); + + this->find_new_earliest (); + + --this->size_; + + return temp; +} + +// Finds a new earliest position + +template void +ACE_Timer_Hash_T::find_new_earliest (void) +{ + for (size_t i = 0; i < this->table_size_; i++) + if (!this->table_[i]->is_empty ()) + if (this->table_[this->earliest_position_]->is_empty () + || this->earliest_time () == ACE_Time_Value::zero + || this->table_[i]->earliest_time () <= this->earliest_time ()) + this->earliest_position_ = i; +} + +// Returns the earliest node without removing it + +template ACE_Timer_Node_T * +ACE_Timer_Hash_T::get_first (void) +{ + ACE_TRACE ("ACE_Timer_Hash_T::get_first"); + + if (this->is_empty ()) + return 0; + + return this->table_[this->earliest_position_]->get_first (); +} + +// Dummy version of expire to get rid of warnings in Sun CC 4.2 + +template int +ACE_Timer_Hash_T::expire () +{ + return ACE_Timer_Queue_T::expire(); +} + +// Specialized expire for Timer Hash + +template int +ACE_Timer_Hash_T::expire (const ACE_Time_Value &cur_time) +{ + ACE_TRACE ("ACE_Timer_Hash_T::expire"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + int number_of_timers_expired = 0; + + ACE_Timer_Node_T *expired; + + // Go through the table and expire anything that can be expired + + for (size_t i = 0; + i < this->table_size_; + i++) + { + while (!this->table_[i]->is_empty () + && this->table_[i]->earliest_time () <= cur_time) + { + expired = this->table_[i]->get_first (); + TYPE type = expired->get_type (); + const void *act = expired->get_act (); + int reclaim = 1; + + // Check if this is an interval timer. + if (expired->get_interval () > ACE_Time_Value::zero) + { + // Make sure that we skip past values that have already + // "expired". + do + expired->set_timer_value (expired->get_timer_value () + + expired->get_interval ()); + while (expired->get_timer_value () <= cur_time); + + // Since this is an interval timer, we need to + // reschedule it. + this->reschedule (expired); + reclaim = 0; + } + + // Now remove the timer from the original table... if + // it's a simple, non-recurring timer, it's got to be + // removed anyway. If it was rescheduled, it's been + // scheduled into the correct table (regardless of whether + // it's the same one or not) already. + this->table_[i]->cancel (expired->get_timer_id ()); + + Hash_Token *h = ACE_reinterpret_cast (Hash_Token *, + ACE_const_cast (void *, + act)); + // Call the functor. + this->upcall (type, + h->act_, + cur_time); + if (reclaim) + { + --this->size_; + delete h; + } + number_of_timers_expired++; + } + } + + return number_of_timers_expired; +} + +#endif /* ACE_TIMER_HASH_T_C */ diff --git a/ace/Timer/Timer_Hash_T.h b/ace/Timer/Timer_Hash_T.h new file mode 100644 index 00000000000..36efdb169d5 --- /dev/null +++ b/ace/Timer/Timer_Hash_T.h @@ -0,0 +1,283 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_Hash_T.h + * + * $Id$ + * + * @author Darrell Brunsch + */ +//============================================================================= + +#ifndef ACE_TIMER_HASH_T_H +#define ACE_TIMER_HASH_T_H +#include "ace/pre.h" + +#include "ace/Timer_Queue_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Free_List.h" + +// Forward declaration. +template +class ACE_Timer_Hash_T; + +/** + * @class ACE_Timer_Hash_Upcall + * + * @brief Functor for Timer_Hash + * + * This class calls up to the Timer Hash's functor from the + * timer queues in the hash table + */ +template +class ACE_Timer_Hash_Upcall +{ +public: + typedef ACE_Timer_Queue_T, + ACE_Null_Mutex> + TIMER_QUEUE; + + /// Default constructor (creates an invalid object, but needs to be here + /// so timer queues using this functor can be constructed) + ACE_Timer_Hash_Upcall (void); + + /// Constructor that specifies a Timer_Hash to call up to + ACE_Timer_Hash_Upcall (ACE_Timer_Queue_T *timer_hash); + + /// This method is called when the timer expires + int timeout (TIMER_QUEUE &timer_queue, + ACE_Event_Handler *handler, + const void *arg, + const ACE_Time_Value &cur_time); + + /// This method is called when the timer is canceled + int cancellation (TIMER_QUEUE &timer_queue, + ACE_Event_Handler *handler); + + /// This method is called when the timer queue is destroyed and + /// the timer is still contained in it + int deletion (TIMER_QUEUE &timer_queue, + ACE_Event_Handler *handler, + const void *arg); + +private: + /// Timer Queue to do the calling up to + ACE_Timer_Queue_T *timer_hash_; + + // = Don't allow these operations for now. + ACE_UNIMPLEMENTED_FUNC (ACE_Timer_Hash_Upcall (const ACE_Timer_Hash_Upcall &)) + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Timer_Hash_Upcall &)) +}; + +/** + * @class ACE_Timer_Hash_Iterator_T + * + * @brief Iterates over an . + * + * This is a generic iterator that can be used to visit every + * node of a timer queue. Be aware that it doesn't transverse + * in the order of timeout values. + */ +template +class ACE_Timer_Hash_Iterator_T : public ACE_Timer_Queue_Iterator_T +{ +public: + /// Constructor. + ACE_Timer_Hash_Iterator_T (ACE_Timer_Hash_T &); + + /// Positions the iterator at the earliest node in the Timer Queue + virtual void first (void); + + /// Positions the iterator at the next node in the Timer Queue + virtual void next (void); + + /// Returns true when there are no more nodes in the sequence + virtual int isdone (void) const; + + /// Returns the node at the current position in the sequence + virtual ACE_Timer_Node_T *item (void); + +protected: + /// Pointer to the that we are iterating over. + ACE_Timer_Hash_T &timer_hash_; + + /// Current position in 's table + size_t position_; + + /// Current iterator used on 's bucket + ACE_Timer_Queue_Iterator_T, ACE_Null_Mutex> *iter_; +}; + +/** + * @class ACE_Timer_Hash_T + * + * @brief Provides a hash table of s as an implementation for + * a timer queue. + * + * This implementation uses a hash table of BUCKETs. The hash + * is based on the time_value of the event. Unlike other Timer + * Queues, ACE_Timer_Hash does not expire events in order. + */ +template +class ACE_Timer_Hash_T : public ACE_Timer_Queue_T +{ +public: + /// Type of iterator + typedef ACE_Timer_Hash_Iterator_T + HASH_ITERATOR; + + /// Iterator is a friend + friend class ACE_Timer_Hash_Iterator_T; + + /// Type inherited from + typedef ACE_Timer_Queue_T INHERITED; + + // = Initialization and termination methods. + /** + * Default constructor. determines the size of the + * hash table. is the instance of the FUNCTOR + * to be used by the buckets. If is 0, a default + * FUNCTOR will be created. + */ + ACE_Timer_Hash_T (size_t table_size, + FUNCTOR *upcall_functor = 0, + ACE_Free_List > *freelist = 0); + + /** + * Default constructor. is the instance of the + * FUNCTOR to be used by the queue. If is 0, Timer + * Hash will create a default FUNCTOR. the freelist of + * timer nodes. If 0, then a default freelist will be created. The default + * size will be ACE_DEFAULT_TIMERS and there will be no preallocation. + */ + ACE_Timer_Hash_T (FUNCTOR *upcall_functor = 0, ACE_Free_List > *freelist = 0); + + /// Destructor + virtual ~ACE_Timer_Hash_T (void); + + /// True if queue is empty, else false. + virtual int is_empty (void) const; + + /// Returns the time of the earlier node in the . + /// Must be called on a non-empty queue. + virtual const ACE_Time_Value &earliest_time (void) const; + + /** + * Schedule that will expire after amount of time, + * which is specified in absolute time. If it expires then is + * passed in as the value to the . If is != to + * then it is used to reschedule the + * automatically, using relative time to the current . + * This method returns a that is a pointer to a token + * which stores information about the event. This can be + * used to cancel the timer before it expires. Returns -1 on + * failure. + */ + virtual long schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval = ACE_Time_Value::zero); + + /** + * Resets the interval of the timer represented by to + * , which is specified in relative time to the current + * . If is equal to + * , the timer will become a non-rescheduling + * timer. Returns 0 if successful, -1 if not. + */ + virtual int reset_interval (long timer_id, + const ACE_Time_Value &interval); + + /** + * Cancel all timer associated with . If is 0 + * then the will be invoked. Returns number of timers + * cancelled. + */ + virtual int cancel (const TYPE &type, + int dont_call_handle_close = 1); + + /** + * Cancel the single timer that matches the value (which + * was returned from the method). If act is non-NULL + * then it will be set to point to the ``magic cookie'' argument + * passed in when the timer was registered. This makes it possible + * to free up the memory and avoid memory leaks. If is + * 0 then the will be invoked. Returns 1 if cancellation + * succeeded and 0 if the wasn't found. + */ + virtual int cancel (long timer_id, + const void **act = 0, + int dont_call_handle_close = 1); + + /** + * Run the for all timers whose values are <= + * . Also accounts for . Returns + * the number of timers canceled. + */ + virtual int expire (void); + + /** + * Run the for all timers whose values are <= . + * This does not account for . Returns the number of + * timers canceled. + */ + virtual int expire (const ACE_Time_Value ¤t_time); + + /// Returns a pointer to this 's iterator. + virtual ACE_Timer_Queue_Iterator_T &iter (void); + + /// Removes the earliest node from the queue and returns it + virtual ACE_Timer_Node_T *remove_first (void); + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Reads the earliest node from the queue and returns it. + virtual ACE_Timer_Node_T *get_first (void); + +private: + /// Reschedule an "interval" . + virtual void reschedule (ACE_Timer_Node_T *); + + /// Finds the earliest node + void find_new_earliest (void); + + /// Keeps track of the size of the queue + size_t size_; + + /// Table of BUCKETS + BUCKET **table_; + + /// Keeps track of the size of table_ + size_t table_size_; + + /// Functor used for the table's timer queues + ACE_Timer_Hash_Upcall table_functor_; + + /// Index to the position with the earliest entry + size_t earliest_position_; + + /// Iterator used to expire timers. + HASH_ITERATOR *iterator_; + + // = Don't allow these operations for now. + ACE_UNIMPLEMENTED_FUNC (ACE_Timer_Hash_T (const ACE_Timer_Hash_T &)) + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Timer_Hash_T &)) +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) && !defined(ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_Hash_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE && !ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Timer_Hash_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TIMER_HASH_T_H */ diff --git a/ace/Timer/Timer_Heap.cpp b/ace/Timer/Timer_Heap.cpp new file mode 100644 index 00000000000..8132c874f10 --- /dev/null +++ b/ace/Timer/Timer_Heap.cpp @@ -0,0 +1,43 @@ +// $Id$ + +#if !defined (ACE_TIMER_HEAP_C) +#define ACE_TIMER_HEAP_C + +#include "ace/Timer_Heap.h" + +ACE_RCSID(ace, Timer_Heap, "$Id$") + +#if defined (ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_Hash.h" +#include "ace/Timer_Heap_T.cpp" +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class + ACE_Timer_Heap_T< + ACE_Event_Handler*, + ACE_Timer_Hash_Upcall< + ACE_Event_Handler*, + ACE_Event_Handler_Handle_Timeout_Upcall, + ACE_Null_Mutex>, + ACE_Null_Mutex>; + +template class + ACE_Timer_Heap_Iterator_T< + ACE_Event_Handler*, + ACE_Timer_Hash_Upcall< + ACE_Event_Handler*, + ACE_Event_Handler_Handle_Timeout_Upcall, + ACE_Null_Mutex>, + ACE_Null_Mutex>; +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ +#endif /* ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Timer_Heap_T, ACE_SYNCH_RECURSIVE_MUTEX>; +template class ACE_Timer_Heap_Iterator_T, ACE_SYNCH_RECURSIVE_MUTEX>; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Timer_Heap_T, ACE_SYNCH_RECURSIVE_MUTEX> +#pragma instantiate ACE_Timer_Heap_Iterator_T, ACE_SYNCH_RECURSIVE_MUTEX> +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + + +#endif /* ACE_TIMER_HEAP_C */ diff --git a/ace/Timer/Timer_Heap.h b/ace/Timer/Timer_Heap.h new file mode 100644 index 00000000000..5db29677f75 --- /dev/null +++ b/ace/Timer/Timer_Heap.h @@ -0,0 +1,38 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_Heap.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_TIMER_HEAP_H +#define ACE_TIMER_HEAP_H +#include "ace/pre.h" + +#include "ace/Timer_Heap_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// The following typedef are here for ease of use and backward +// compatibility. + +typedef ACE_Timer_Heap_T, + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Timer_Heap; + +typedef ACE_Timer_Heap_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Timer_Heap_Iterator; + +#include "ace/post.h" +#endif /* ACE_TIMER_HEAP_H */ diff --git a/ace/Timer/Timer_Heap_T.cpp b/ace/Timer/Timer_Heap_T.cpp new file mode 100644 index 00000000000..c3596238621 --- /dev/null +++ b/ace/Timer/Timer_Heap_T.cpp @@ -0,0 +1,785 @@ +// $Id$ + +#ifndef ACE_TIMER_HEAP_T_C +#define ACE_TIMER_HEAP_T_C + +#include "ace/Timer_Heap_T.h" +#include "ace/Log_Msg.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +ACE_RCSID(ace, Timer_Heap_T, "$Id$") + +// Define some simple macros to clarify the code. +#define ACE_HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) +#define ACE_HEAP_LCHILD(X) (((X)+(X))+1) + +// Constructor that takes in an to iterate over. + +template +ACE_Timer_Heap_Iterator_T::ACE_Timer_Heap_Iterator_T (ACE_Timer_Heap_T &heap) + : timer_heap_ (heap) +{ + ACE_TRACE ("ACE_Timer_Heap_Iterator_T::ACE_Timer_Heap_Iterator"); + this->first(); +} + +template +ACE_Timer_Heap_Iterator_T::~ACE_Timer_Heap_Iterator_T (void) +{ +} + +// Positions the iterator at the first node in the heap array + +template void +ACE_Timer_Heap_Iterator_T::first (void) +{ + this->position_ = 0; +} + +// Positions the iterator at the next node in the heap array + +template void +ACE_Timer_Heap_Iterator_T::next (void) +{ + if (this->position_ != this->timer_heap_.cur_size_) + this->position_++; +} + +// Returns true the is at the end of the heap array + +template int +ACE_Timer_Heap_Iterator_T::isdone (void) const +{ + return this->position_ == this->timer_heap_.cur_size_; +} + +// Returns the node at the current position in the heap or 0 if at the end + +template ACE_Timer_Node_T * +ACE_Timer_Heap_Iterator_T::item (void) +{ + if (this->position_ != this->timer_heap_.cur_size_) + return this->timer_heap_.heap_[this->position_]; + return 0; +} + +// Constructor +// Note that timer_ids_curr_ and timer_ids_min_free_ both start at 0. +// Since timer IDs are assigned by first incrementing the timer_ids_curr_ +// value, the first ID assigned will be 1 (just as in the previous design). +// When it's time to wrap, the next ID given out will be 0. +template +ACE_Timer_Heap_T::ACE_Timer_Heap_T (size_t size, + int preallocate, + FUNCTOR *upcall_functor, + ACE_Free_List > *freelist) + : ACE_Timer_Queue_T (upcall_functor, freelist), + max_size_ (size), + cur_size_ (0), + cur_limbo_ (0), + timer_ids_curr_ (0), + timer_ids_min_free_ (0), + preallocated_nodes_ (0), + preallocated_nodes_freelist_ (0) +{ + ACE_TRACE ("ACE_Timer_Heap_T::ACE_Timer_Heap_T"); + + // Create the heap array. + ACE_NEW (this->heap_, + ACE_Timer_Node_T *[size]); + + // Create the parallel + ACE_NEW (this->timer_ids_, + long[size]); + + // Initialize the "freelist," which uses negative values to + // distinguish freelist elements from "pointers" into the + // array. + for (size_t i = 0; i < size; i++) + this->timer_ids_[i] = -1; + + if (preallocate) + { + ACE_NEW (this->preallocated_nodes_, + ACE_Timer_Node_T[size]); + + // Add allocated array to set of such arrays for deletion on + // cleanup. + this->preallocated_node_set_.insert (this->preallocated_nodes_); + + // Form the freelist by linking the next_ pointers together. + for (size_t j = 1; j < size; j++) + this->preallocated_nodes_[j - 1].set_next (&this->preallocated_nodes_[j]); + + // NULL-terminate the freelist. + this->preallocated_nodes_[size - 1].set_next (0); + + // Assign the freelist pointer to the front of the list. + this->preallocated_nodes_freelist_ = + &this->preallocated_nodes_[0]; + } + + ACE_NEW (iterator_, + HEAP_ITERATOR (*this)); +} + +// Note that timer_ids_curr_ and timer_ids_min_free_ both start at 0. +// Since timer IDs are assigned by first incrementing the timer_ids_curr_ +// value, the first ID assigned will be 1 (just as in the previous design). +// When it's time to wrap, the next ID given out will be 0. +template +ACE_Timer_Heap_T::ACE_Timer_Heap_T (FUNCTOR *upcall_functor, + ACE_Free_List > *freelist) + : ACE_Timer_Queue_T (upcall_functor, freelist), + max_size_ (ACE_DEFAULT_TIMERS), + cur_size_ (0), + cur_limbo_ (0), + timer_ids_curr_ (0), + timer_ids_min_free_ (0), + preallocated_nodes_ (0), + preallocated_nodes_freelist_ (0) +{ + ACE_TRACE ("ACE_Timer_Heap_T::ACE_Timer_Heap_T"); + + // Create the heap array. +#if defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) + ACE_NEW (this->heap_, + ACE_Timer_Node_T *[ACE_DEFAULT_TIMERS]); +#else + ACE_NEW (this->heap_, + ACE_Timer_Node_T *[this->max_size_]); +#endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) */ + + // Create the parallel array. + ACE_NEW (this->timer_ids_, + long[this->max_size_]); + + // Initialize the "freelist," which uses negative values to + // distinguish freelist elements from "pointers" into the + // array. + for (size_t i = 0; i < this->max_size_; i++) + this->timer_ids_[i] = -1; + + ACE_NEW (iterator_, + HEAP_ITERATOR (*this)); +} + +template +ACE_Timer_Heap_T::~ACE_Timer_Heap_T (void) +{ + ACE_TRACE ("ACE_Timer_Heap_T::~ACE_Timer_Heap_T"); + + delete iterator_; + + // Clean up all the nodes still in the queue + for (size_t i = 0; i < this->cur_size_; i++) + { + this->upcall_functor ().deletion (*this, + this->heap_[i]->get_type (), + this->heap_[i]->get_act ()); + this->free_node (this->heap_[i]); + } + + delete [] this->heap_; + delete [] this->timer_ids_; + + // clean up any preallocated timer nodes + if (preallocated_nodes_ != 0) + { + ACE_Unbounded_Set_Iterator *> + set_iterator (this->preallocated_node_set_); + + for (ACE_Timer_Node_T **entry = 0; + set_iterator.next (entry) !=0; + set_iterator.advance ()) + delete [] *entry; + } +} + +template int +ACE_Timer_Heap_T::pop_freelist (void) +{ + ACE_TRACE ("ACE_Timer_Heap_T::pop_freelist"); + + // Scan for a free timer ID. Note that since this function is called + // _after_ the check for a full timer heap, we are guaranteed to find + // a free ID, even if we need to wrap around and start reusing freed IDs. + // On entry, the curr_ index is at the previous ID given out; start + // up where we left off last time. + // NOTE - a timer_ids_ slot with -2 is out of the heap, but not freed. + // It must be either freed (free_node) or rescheduled (reschedule). + ++this->timer_ids_curr_; + while (this->timer_ids_curr_ < this->max_size_ && + (this->timer_ids_[this->timer_ids_curr_] >= 0 || + this->timer_ids_[this->timer_ids_curr_] == -2 )) + ++this->timer_ids_curr_; + if (this->timer_ids_curr_ == this->max_size_) + { + ACE_ASSERT (this->timer_ids_min_free_ < this->max_size_); + this->timer_ids_curr_ = this->timer_ids_min_free_; + // We restarted the free search at min. Since min won't be + // free anymore, and curr_ will just keep marching up the list + // on each successive need for an ID, reset min_free_ to the + // size of the list until an ID is freed that curr_ has already + // gone past (see push_freelist). + this->timer_ids_min_free_ = this->max_size_; + } + + // We need to truncate this to for backwards compatibility. + int new_id = ACE_static_cast (int, + this->timer_ids_curr_); + return new_id; +} + +template void +ACE_Timer_Heap_T::push_freelist (int old_id) +{ + ACE_TRACE ("ACE_Timer_Heap_T::push_freelist"); + + // Since this ID has already been checked by one of the public + // functions, it's safe to cast it here. + size_t oldid = size_t (old_id); + + // The freelist values in the are negative, so set the + // freed entry back to 'free'. If this is the new lowest value free + // timer ID that curr_ won't see on it's normal march through the list, + // remember it. + ACE_ASSERT (this->timer_ids_[oldid] >= 0 || this->timer_ids_[oldid] == -2); + if (this->timer_ids_[oldid] == -2) + --this->cur_limbo_; + else + --this->cur_size_; + this->timer_ids_[oldid] = -1; + if (oldid < this->timer_ids_min_free_ && oldid <= this->timer_ids_curr_) + this->timer_ids_min_free_ = oldid; + return; +} + +template int +ACE_Timer_Heap_T::timer_id (void) +{ + ACE_TRACE ("ACE_Timer_Heap_T::timer_id"); + + // Return the next item off the freelist and use it as the timer id. + return this->pop_freelist (); +} + +// Checks if queue is empty. + +template int +ACE_Timer_Heap_T::is_empty (void) const +{ + ACE_TRACE ("ACE_Timer_Heap_T::is_empty"); + return this->cur_size_ == 0; +} + +template ACE_Timer_Queue_Iterator_T & +ACE_Timer_Heap_T::iter (void) +{ + this->iterator_->first (); + return *this->iterator_; +} + +// Returns earliest time in a non-empty queue. + +template const ACE_Time_Value & +ACE_Timer_Heap_T::earliest_time (void) const +{ + ACE_TRACE ("ACE_Timer_Heap_T::earliest_time"); + return this->heap_[0]->get_timer_value (); +} + +template void +ACE_Timer_Heap_T::dump (void) const +{ + ACE_TRACE ("ACE_Timer_Heap_T::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nmax_size_ = %d"), this->max_size_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncur_size_ = %d"), this->cur_size_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncur_limbo_= %d"), this->cur_limbo_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nids_curr_ = %d"), + this->timer_ids_curr_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nmin_free_ = %d"), + this->timer_ids_min_free_)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nheap_ = \n"))); + + for (size_t i = 0; i < this->cur_size_; i++) + { + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("%d\n"), + i)); + this->heap_[i]->dump (); + } + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ntimer_ids_ = \n"))); + + for (size_t j = 0; j < this->max_size_; j++) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("%d\t%d\n"), + j, + this->timer_ids_[j])); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template void +ACE_Timer_Heap_T::copy (int slot, + ACE_Timer_Node_T *moved_node) +{ + // Insert into its new location in the heap. + this->heap_[slot] = moved_node; + + ACE_ASSERT (moved_node->get_timer_id () >= 0 + && moved_node->get_timer_id () < (int) this->max_size_); + + // Update the corresponding slot in the parallel array. + this->timer_ids_[moved_node->get_timer_id ()] = slot; +} + +// Remove the slot'th timer node from the heap, but do not reclaim its +// timer ID or change the size of this timer heap object. The caller of +// this function must call either free_node (to reclaim the timer ID +// and the timer node memory, as well as decrement the size of the queue) +// or reschedule (to reinsert the node in the heap at a new time). +template ACE_Timer_Node_T * +ACE_Timer_Heap_T::remove (size_t slot) +{ + ACE_Timer_Node_T *removed_node = + this->heap_[slot]; + + // NOTE - the cur_size_ is being decremented since the queue has one + // less active timer in it. However, this ACE_Timer_Node is not being + // freed, and there is still a place for it in timer_ids_ (the timer ID + // is not being relinquished). The node can still be rescheduled, or + // it can be freed via free_node. + --this->cur_size_; + + // Only try to reheapify if we're not deleting the last entry. + + if (slot < this->cur_size_) + { + ACE_Timer_Node_T *moved_node = + this->heap_[this->cur_size_]; + + // Move the end node to the location being removed and update + // the corresponding slot in the parallel array. + this->copy (slot, moved_node); + + // If the time_value_> is great than or equal its + // parent it needs be moved down the heap. + size_t parent = ACE_HEAP_PARENT (slot); + + if (moved_node->get_timer_value () + >= this->heap_[parent]->get_timer_value ()) + this->reheap_down (moved_node, + slot, + ACE_HEAP_LCHILD (slot)); + else + this->reheap_up (moved_node, + slot, + parent); + } + + this->timer_ids_[removed_node->get_timer_id ()] = -2; + ++this->cur_limbo_; + return removed_node; +} + +template void +ACE_Timer_Heap_T::reheap_down (ACE_Timer_Node_T *moved_node, + size_t slot, + size_t child) +{ + // Restore the heap property after a deletion. + + while (child < this->cur_size_) + { + // Choose the smaller of the two children. + if (child + 1 < this->cur_size_ + && this->heap_[child + 1]->get_timer_value () + < this->heap_[child]->get_timer_value ()) + child++; + + // Perform a if the child has a larger timeout value than + // the . + if (this->heap_[child]->get_timer_value () + < moved_node->get_timer_value ()) + { + this->copy (slot, + this->heap_[child]); + slot = child; + child = ACE_HEAP_LCHILD (child); + } + else + // We've found our location in the heap. + break; + } + + this->copy (slot, moved_node); +} + +template void +ACE_Timer_Heap_T::reheap_up (ACE_Timer_Node_T *moved_node, + size_t slot, + size_t parent) +{ + // Restore the heap property after an insertion. + + while (slot > 0) + { + // If the parent node is greater than the we need + // to copy it down. + if (moved_node->get_timer_value () + < this->heap_[parent]->get_timer_value ()) + { + this->copy (slot, this->heap_[parent]); + slot = parent; + parent = ACE_HEAP_PARENT (slot); + } + else + break; + } + + // Insert the new node into its proper resting place in the heap and + // update the corresponding slot in the parallel array. + this->copy (slot, + moved_node); +} + +template void +ACE_Timer_Heap_T::insert (ACE_Timer_Node_T *new_node) +{ + if (this->cur_size_ + 2 >= this->max_size_) + this->grow_heap (); + + this->reheap_up (new_node, + this->cur_size_, + ACE_HEAP_PARENT (this->cur_size_)); + this->cur_size_++; +} + +template void +ACE_Timer_Heap_T::grow_heap (void) +{ + // All the containers will double in size from max_size_ + size_t new_size = this->max_size_ * 2; + + // First grow the heap itself. + + ACE_Timer_Node_T **new_heap = 0; + +#if defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) + ACE_NEW (new_heap, + ACE_Timer_Node_T *[1024]); +#else + ACE_NEW (new_heap, + ACE_Timer_Node_T *[new_size]); +#endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) */ + ACE_OS::memcpy (new_heap, + this->heap_, + this->max_size_ * sizeof *new_heap); + delete [] this->heap_; + this->heap_ = new_heap; + + // Grow the array of timer ids. + + long *new_timer_ids = 0; + + ACE_NEW (new_timer_ids, + long[new_size]); + + ACE_OS::memcpy (new_timer_ids, + this->timer_ids_, + this->max_size_ * sizeof (long)); + + delete [] timer_ids_; + this->timer_ids_ = new_timer_ids; + + // And add the new elements to the end of the "freelist". + for (size_t i = this->max_size_; i < new_size; i++) + this->timer_ids_[i] = -((long) (i + 1)); + + // Grow the preallocation array (if using preallocation) + if (this->preallocated_nodes_ != 0) + { + // Create a new array with max_size elements to link in to + // existing list. +#if defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) + ACE_NEW (this->preallocated_nodes_, + ACE_Timer_Node_T[88]); +#else + ACE_NEW (this->preallocated_nodes_, + ACE_Timer_Node_T[this->max_size_]); +#endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) */ + + // Add it to the set for later deletion + this->preallocated_node_set_.insert (this->preallocated_nodes_); + + // Link new nodes together (as for original list). + for (size_t k = 1; k < this->max_size_; k++) + this->preallocated_nodes_[k - 1].set_next (&this->preallocated_nodes_[k]); + + // NULL-terminate the new list. + this->preallocated_nodes_[this->max_size_ - 1].set_next (0); + + // Link new array to the end of the existling list. + if (this->preallocated_nodes_freelist_ == 0) + this->preallocated_nodes_freelist_ = + &preallocated_nodes_[0]; + else + { + ACE_Timer_Node_T *previous = + this->preallocated_nodes_freelist_; + + for (ACE_Timer_Node_T *current = this->preallocated_nodes_freelist_->get_next (); + current != 0; + current = current->get_next ()) + previous = current; + + previous->set_next (&this->preallocated_nodes_[0]); + } + } + + this->max_size_ = new_size; +} + +// Reschedule a periodic timer. This function must be called with the +// mutex lock held. + +template void +ACE_Timer_Heap_T::reschedule (ACE_Timer_Node_T *expired) +{ + ACE_TRACE ("ACE_Timer_Heap_T::reschedule"); + + // If we are rescheduling, then the most recent call was to + // remove_first (). That called remove () to remove the node from the + // heap, but did not free the timer ID. The ACE_Timer_Node still has + // its assigned ID - just needs to be inserted at the new proper + // place, and the heap restored properly. + if (this->timer_ids_[expired->get_timer_id ()] == -2) + --this->cur_limbo_; + this->insert (expired); +} + +template ACE_Timer_Node_T * +ACE_Timer_Heap_T::alloc_node (void) +{ + ACE_Timer_Node_T *temp = 0; + + // Only allocate a node if we are *not* using the preallocated heap. + if (this->preallocated_nodes_ == 0) + ACE_NEW_RETURN (temp, + ACE_Timer_Node_T, + 0); + else + { + // check to see if the heap needs to grow + if (this->preallocated_nodes_freelist_ == 0) + this->grow_heap (); + + temp = this->preallocated_nodes_freelist_; + + // Remove the first element from the freelist. + this->preallocated_nodes_freelist_ = + this->preallocated_nodes_freelist_->get_next (); + } + return temp; +} + +template void +ACE_Timer_Heap_T::free_node (ACE_Timer_Node_T *node) +{ + + // Return this timer id to the freelist. + this->push_freelist (node->get_timer_id ()); + + // Only free up a node if we are *not* using the preallocated heap. + if (this->preallocated_nodes_ == 0) + delete node; + else + { + node->set_next (this->preallocated_nodes_freelist_); + this->preallocated_nodes_freelist_ = node; + } +} + +// Insert a new timer that expires at time future_time; if interval is +// > 0, the handler will be reinvoked periodically. + +template long +ACE_Timer_Heap_T::schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &future_time, + const ACE_Time_Value &interval) +{ + ACE_TRACE ("ACE_Timer_Heap_T::schedule"); + + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + if ((this->cur_size_ + this->cur_limbo_) < this->max_size_) + { + // Obtain the next unique sequence number. + int timer_id = this->timer_id (); + + // Obtain the memory to the new node. + ACE_Timer_Node_T *temp = 0; + + ACE_ALLOCATOR_RETURN (temp, + this->alloc_node (), + -1); + temp->set (type, + act, + future_time, + interval, + 0, + timer_id); + + this->insert (temp); + return timer_id; + } + else + return -1; +} + +// Locate and remove the single timer with a value of from +// the timer queue. + +template int +ACE_Timer_Heap_T::cancel (long timer_id, + const void **act, + int dont_call) +{ + ACE_TRACE ("ACE_Timer_Heap_T::cancel"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Locate the ACE_Timer_Node that corresponds to the timer_id. + + // Check to see if the timer_id is out of range + if (timer_id < 0 + || (size_t) timer_id > this->max_size_) + return 0; + + long timer_node_slot = this->timer_ids_[timer_id]; + + // Check to see if timer_id is still valid. + if (timer_node_slot < 0) + return 0; + + if (timer_id != this->heap_[timer_node_slot]->get_timer_id ()) + { + ACE_ASSERT (timer_id == this->heap_[timer_node_slot]->get_timer_id ()); + return 0; + } + else + { + ACE_Timer_Node_T *temp = + this->remove (timer_node_slot); + + if (dont_call == 0) + // Call the close hook. + this->upcall_functor ().cancellation (*this, + temp->get_type ()); + + if (act != 0) + *act = temp->get_act (); + + this->free_node (temp); + return 1; + } +} + +// Locate and update the inteval on the timer_id + +template int +ACE_Timer_Heap_T::reset_interval (long timer_id, + const ACE_Time_Value &interval) +{ + ACE_TRACE ("ACE_Timer_Heap_T::reset_interval"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Locate the ACE_Timer_Node that corresponds to the timer_id. + + // Check to see if the timer_id is out of range + if (timer_id < 0 + || (size_t) timer_id > this->max_size_) + return -1; + + long timer_node_slot = this->timer_ids_[timer_id]; + + // Check to see if timer_id is still valid. + if (timer_node_slot < 0) + return -1; + + if (timer_id != this->heap_[timer_node_slot]->get_timer_id ()) + { + ACE_ASSERT (timer_id == this->heap_[timer_node_slot]->get_timer_id ()); + return -1; + } + else + { + // Reset the timer interval + this->heap_[timer_node_slot]->set_interval (interval); + return 0; + } +} + +// Locate and remove all values of from the timer queue. + +template int +ACE_Timer_Heap_T::cancel (const TYPE &type, + int dont_call) +{ + ACE_TRACE ("ACE_Timer_Heap_T::cancel"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + int number_of_cancellations = 0; + + // Try to locate the ACE_Timer_Node that matches the timer_id. + + for (size_t i = 0; i < this->cur_size_; ) + { + if (this->heap_[i]->get_type () == type) + { + ACE_Timer_Node_T *temp = this->remove (i); + + number_of_cancellations++; + + this->free_node (temp); + } + else + i++; + } + + if (dont_call == 0) + this->upcall_functor ().cancellation (*this, type); + + return number_of_cancellations; +} + +// Returns the earliest node or returns 0 if the heap is empty. + +template ACE_Timer_Node_T * +ACE_Timer_Heap_T::remove_first (void) +{ + ACE_TRACE ("ACE_Timer_Heap_T::remove_first"); + + if (this->cur_size_ == 0) + return 0; + + return this->remove (0); +} + +template ACE_Timer_Node_T * +ACE_Timer_Heap_T::get_first (void) +{ + ACE_TRACE ("ACE_Timer_Heap_T::get_first"); + + return this->cur_size_ == 0 ? 0 : this->heap_[0]; +} + +#endif /* ACE_TIMER_HEAP_T_C */ diff --git a/ace/Timer/Timer_Heap_T.h b/ace/Timer/Timer_Heap_T.h new file mode 100644 index 00000000000..6039225e797 --- /dev/null +++ b/ace/Timer/Timer_Heap_T.h @@ -0,0 +1,329 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_Heap_T.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_TIMER_HEAP_T_H +#define ACE_TIMER_HEAP_T_H +#include "ace/pre.h" + +#include "ace/Timer_Queue_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Free_List.h" +#include "ace/Unbounded_Set.h" + +// Forward declaration +template +class ACE_Timer_Heap_T; + +/** + * @class ACE_Timer_Heap_Iterator_T + * + * @brief Iterates over an . + * + * This is a generic iterator that can be used to visit every + * node of a timer queue. Be aware that it doesn't transverse + * in the order of timeout values. + */ +template +class ACE_Timer_Heap_Iterator_T : public ACE_Timer_Queue_Iterator_T +{ +public: + /// Constructor. + ACE_Timer_Heap_Iterator_T (ACE_Timer_Heap_T &); + + /// Destructor. + ~ACE_Timer_Heap_Iterator_T (void); + + /// Positions the iterator at the earliest node in the Timer Queue + virtual void first (void); + + /// Positions the iterator at the next node in the Timer Queue + virtual void next (void); + + /// Returns true when there are no more nodes in the sequence + virtual int isdone (void) const; + + /// Returns the node at the current position in the sequence + virtual ACE_Timer_Node_T *item (void); + +protected: + /// Pointer to the that we are iterating over. + ACE_Timer_Heap_T &timer_heap_; + + /// Position in the array where the iterator is at + size_t position_; +}; + +/** + * @class ACE_Timer_Heap_T + * + * @brief Provides a very fast and predictable timer implementation. + * + * This implementation uses a heap-based callout queue of + * absolute times. Therefore, in the average and worst case, + * scheduling, canceling, and expiring timers is O(log N) (where + * N is the total number of timers). In addition, we can also + * preallocate as many @c ACE_Timer_Node objects as there are slots + * in the heap. This allows us to completely remove the need for + * dynamic memory allocation, which is important for real-time + * systems. + */ +template +class ACE_Timer_Heap_T : public ACE_Timer_Queue_T +{ +public: + typedef ACE_Timer_Heap_Iterator_T HEAP_ITERATOR; + friend class ACE_Timer_Heap_Iterator_T; + + typedef ACE_Timer_Queue_T INHERITED; + + // = Initialization and termination methods. + /** + * The Constructor creates a heap with specified number of elements. + * This can also take in a upcall functor and freelist (if 0, then + * defaults will be created). + * + * @param size size_t, the maximum number of timers that can be + * inserted into the new object. + * @param preallocated int (default 0), if non-0 then all the memory + * for the @c ACE_Timer_Node objects will be pre-allocated. This saves + * time and is more predictable (though it requires more space). + * Otherwise, timer nodes are allocated as needed. + */ + ACE_Timer_Heap_T (size_t size, + int preallocated = 0, + FUNCTOR *upcall_functor = 0, + ACE_Free_List > *freelist = 0); + + /** + * Default constructor. @c upcall_functor is the instance of the + * FUNCTOR to be used by the queue. If @c upcall_functor is 0, Timer + * Heap will create a default FUNCTOR. @c freelist is the freelist of + * timer nodes. If 0, then a default freelist will be created. The default + * size will be ACE_DEFAULT_TIMERS and there will be no preallocation. + */ + ACE_Timer_Heap_T (FUNCTOR *upcall_functor = 0, + ACE_Free_List > *freelist = 0); + + /// Destructor. + virtual ~ACE_Timer_Heap_T (void); + + /// True if heap is empty, else false. + virtual int is_empty (void) const; + + /// Returns the time of the earliest node in the Timer_Queue. + /// Must be called on a non-empty queue. + virtual const ACE_Time_Value &earliest_time (void) const; + + /** + * Schedule a timer that may optionally auto-reset. + * Schedule that will expire after amount of time, + * which is specified in absolute time. If it expires then is + * passed in as the value to the . If is != to + * then it is used to reschedule the + * automatically, using relative time to the current . + * This method returns a that uniquely identifies the the + * entry in an internal list. This can be used to + * cancel the timer before it expires. The cancellation ensures + * that are unique up to values of greater than 2 + * billion timers. As long as timers don't stay around longer than + * this there should be no problems with accidentally deleting the + * wrong timer. Returns -1 on failure (which is guaranteed never to + * be a valid ). + */ + virtual long schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval = ACE_Time_Value::zero); + + /** + * Resets the interval of the timer represented by to + * , which is specified in relative time to the current + * . If is equal to + * , the timer will become a non-rescheduling + * timer. Returns 0 if successful, -1 if not. + */ + virtual int reset_interval (long timer_id, + const ACE_Time_Value &interval); + + /** + * Cancel all timer associated with . If is 0 + * then the will be invoked. Returns number of timers + * cancelled. + */ + virtual int cancel (const TYPE &type, + int dont_call_handle_close = 1); + + /** + * Cancel the single timer that matches the value (which + * was returned from the method). If act is non-NULL + * then it will be set to point to the ``magic cookie'' argument + * passed in when the timer was registered. This makes it possible + * to free up the memory and avoid memory leaks. If is + * 0 then the will be invoked. Returns 1 if cancellation + * succeeded and 0 if the wasn't found. + */ + virtual int cancel (long timer_id, + const void **act = 0, + int dont_call_handle_close = 1); + + /// Returns a pointer to this 's iterator. + virtual ACE_Timer_Queue_Iterator_T &iter (void); + + /// Removes the earliest node from the queue and returns it. Note that + /// the timer is removed from the heap, but is not freed, and its ID + /// is not reclaimed. The caller is responsible for calling either + /// @c reschedule or @c free_node after this function returns. Thus, + /// this function is for support of @c ACE_Timer_Queue::expire and + /// should not be used unadvisedly in other conditions. + ACE_Timer_Node_T *remove_first (void); + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Reads the earliest node from the queue and returns it. + virtual ACE_Timer_Node_T *get_first (void); + +protected: + /// Reschedule an "interval" . + virtual void reschedule (ACE_Timer_Node_T *); + + /// Factory method that allocates a new node (uses operator new if + /// we're *not* preallocating, otherwise uses an internal freelist). + virtual ACE_Timer_Node_T *alloc_node (void); + + /** + * Factory method that frees a previously allocated node (uses + * operator delete if we're *not* preallocating, otherwise uses an + * internal freelist). + */ + virtual void free_node (ACE_Timer_Node_T *); + +private: + /// Remove and return the th and restore the + /// heap property. + ACE_Timer_Node_T *remove (size_t slot); + + /// Insert into the heap and restore the heap property. + void insert (ACE_Timer_Node_T *new_node); + + /** + * Doubles the size of the heap and the corresponding timer_ids array. + * If preallocation is used, will also double the size of the + * preallocated array of ACE_Timer_Nodes. + */ + void grow_heap (void); + + /// Restore the heap property, starting at . + void reheap_up (ACE_Timer_Node_T *new_node, + size_t slot, + size_t parent); + + /// Restore the heap property, starting at . + void reheap_down (ACE_Timer_Node_T *moved_node, + size_t slot, + size_t child); + + /// Copy into the slot of and move + /// into the corresponding slot in the array. + void copy (int slot, ACE_Timer_Node_T *moved_node); + + /** + * Returns a timer id that uniquely identifies this timer. This id + * can be used to cancel a timer via the method. The + * timer id returned from this method will never == -1 to avoid + * conflicts with other failure return values. + */ + int timer_id (void); + + /// Pops and returns a new timer id from the freelist. + int pop_freelist (void); + + /// Pushes onto the freelist. + void push_freelist (int old_id); + + /// Maximum size of the heap. + size_t max_size_; + + /// Current size of the heap. + size_t cur_size_; + + /// Number of heap entries in transition (removed from the queue, but + /// not freed) and may be rescheduled or freed. + size_t cur_limbo_; + + /// Iterator used to expire timers. + HEAP_ITERATOR *iterator_; + + /** + * Current contents of the Heap, which is organized as a "heap" of + * *'s. In this context, a heap is a "partially + * ordered, almost complete" binary tree, which is stored in an + * array. + */ + ACE_Timer_Node_T **heap_; + + /** + * An array of "pointers" that allows each in the + * to be located in O(1) time. Basically, + * contains the slot in the array where an + * * with timer id resides. Thus, the timer id passed back from + * is really a slot into the array. The + * array serves two purposes: negative values are + * indications of free timer IDs, whereas positive values are + * "pointers" into the array for assigned timer IDs. + */ + long *timer_ids_; + + /// "Pointer" to the element in the array that was + /// last given out as a timer ID. + size_t timer_ids_curr_; + + /// Index representing the lowest timer ID that has been freed. When + /// the timer_ids_next_ value wraps around, it starts back at this + /// point. + size_t timer_ids_min_free_; + + /** + * If this is non-0, then we preallocate number of + * objects in order to reduce dynamic allocation + * costs. In auto-growing implementation, this points to the + * last array of nodes allocated. + */ + ACE_Timer_Node_T *preallocated_nodes_; + + /// This points to the head of the freelist, + /// which is organized as a stack. + ACE_Timer_Node_T *preallocated_nodes_freelist_; + + /// Set of pointers to the arrays of preallocated timer nodes. + /// Used to delete the allocated memory when required. + ACE_Unbounded_Set *> preallocated_node_set_; + + // = Don't allow these operations for now. + ACE_UNIMPLEMENTED_FUNC (ACE_Timer_Heap_T (const ACE_Timer_Heap_T &)) + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Timer_Heap_T &)) +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) && !defined(ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_Heap_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE && !ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Timer_Heap_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TIMER_HEAP_T_H */ diff --git a/ace/Timer/Timer_List.cpp b/ace/Timer/Timer_List.cpp new file mode 100644 index 00000000000..3834178a0b0 --- /dev/null +++ b/ace/Timer/Timer_List.cpp @@ -0,0 +1,44 @@ +// $Id$ + +#if !defined (ACE_TIMER_LIST_C) +#define ACE_TIMER_LIST_C + +#include "ace/Timer_List.h" + +ACE_RCSID(ace, Timer_List, "$Id$") + +#if defined (ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_Hash.h" +#include "ace/Timer_List_T.cpp" + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class + ACE_Timer_List_T< + ACE_Event_Handler*, + ACE_Timer_Hash_Upcall< + ACE_Event_Handler*, + ACE_Event_Handler_Handle_Timeout_Upcall, + ACE_Null_Mutex>, + ACE_Null_Mutex>; + +template class +ACE_Timer_List_Iterator_T< + ACE_Event_Handler*, + ACE_Timer_Hash_Upcall< + ACE_Event_Handler*, + ACE_Event_Handler_Handle_Timeout_Upcall, + ACE_Null_Mutex>, + ACE_Null_Mutex>; +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ +#endif /* ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Timer_List_T, ACE_SYNCH_RECURSIVE_MUTEX>; +template class ACE_Timer_List_Iterator_T, ACE_SYNCH_RECURSIVE_MUTEX>; +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#pragma instantiate ACE_Timer_List_T, ACE_SYNCH_RECURSIVE_MUTEX> +#pragma instantiate ACE_Timer_List_Iterator_T, ACE_SYNCH_RECURSIVE_MUTEX> +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + + +#endif /* ACE_TIMER_LIST_C */ diff --git a/ace/Timer/Timer_List.h b/ace/Timer/Timer_List.h new file mode 100644 index 00000000000..a9586061307 --- /dev/null +++ b/ace/Timer/Timer_List.h @@ -0,0 +1,38 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_List.h + * + * $Id$ + * + * @author Doug Schmidt + */ +//============================================================================= + + +#ifndef ACE_TIMER_LIST_H +#define ACE_TIMER_LIST_H +#include "ace/pre.h" + +#include "ace/Timer_List_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// The following typedef are here for ease of use and backward +// compatibility. + +typedef ACE_Timer_List_T, + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Timer_List; + +typedef ACE_Timer_List_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Timer_List_Iterator; + +#include "ace/post.h" +#endif /* ACE_TIMER_LIST_H */ diff --git a/ace/Timer/Timer_List_T.cpp b/ace/Timer/Timer_List_T.cpp new file mode 100644 index 00000000000..710521bcc05 --- /dev/null +++ b/ace/Timer/Timer_List_T.cpp @@ -0,0 +1,342 @@ +// $Id$ + +#ifndef ACE_TIMER_LIST_T_C +#define ACE_TIMER_LIST_T_C + +#include "ace/Timer_List_T.h" +#include "ace/Log_Msg.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +ACE_RCSID(ace, Timer_List_T, "$Id$") + +// Default Constructor + +template +ACE_Timer_List_Iterator_T::ACE_Timer_List_Iterator_T (ACE_Timer_List_T &timer_list) + : timer_list_ (timer_list) +{ + this->first(); + // Nothing +} + +template +ACE_Timer_List_Iterator_T::~ACE_Timer_List_Iterator_T (void) +{ +} + +// Positions the iterator at the node right after the dummy node + +template void +ACE_Timer_List_Iterator_T::first (void) +{ + this->position_ = this->timer_list_.head_->get_next (); +} + +// Positions the iterator at the next node in the Timer Queue + +template void +ACE_Timer_List_Iterator_T::next (void) +{ + // Make sure that if we are at the end, we don't wrap around + if (this->position_ != this->timer_list_.head_) + this->position_ = this->position_->get_next (); +} + +// Returns true when we are at + +template int +ACE_Timer_List_Iterator_T::isdone (void) const +{ + return this->position_ == this->timer_list_.head_; +} + +// Returns the node at or 0 if we are at the end + +template ACE_Timer_Node_T * +ACE_Timer_List_Iterator_T::item (void) +{ + if (this->position_ != this->timer_list_.head_) + return this->position_; + return 0; +} + +// Return our instance of the iterator + +template ACE_Timer_Queue_Iterator_T & +ACE_Timer_List_T::iter (void) +{ + this->iterator_->first (); + return *this->iterator_; +} + +// Create an empty list. + +template +ACE_Timer_List_T::ACE_Timer_List_T (FUNCTOR *upcall_functor, + ACE_Free_List > *freelist) + : ACE_Timer_Queue_T (upcall_functor, freelist), + head_ (new ACE_Timer_Node_T), + timer_id_ (0) +{ + ACE_TRACE ("ACE_Timer_List_T::ACE_Timer_List"); + + this->head_->set_next (this->head_); + this->head_->set_prev (this->head_); + + ACE_NEW (iterator_, + LIST_ITERATOR (*this)); +} + + +// Checks if list is empty. + +template int +ACE_Timer_List_T::is_empty (void) const +{ + ACE_TRACE ("ACE_Timer_List_T::is_empty"); + return this->head_->get_next () == this->head_; +} + + +// Returns earliest time in a non-empty list. + +template const ACE_Time_Value & +ACE_Timer_List_T::earliest_time (void) const +{ + ACE_TRACE ("ACE_Timer_List_T::earliest_time"); + return this->head_->get_next ()->get_timer_value (); +} + + +// Remove all remaining items in the list. + +template +ACE_Timer_List_T::~ACE_Timer_List_T (void) +{ + ACE_TRACE ("ACE_Timer_List_T::~ACE_Timer_List_T"); + ACE_MT (ACE_GUARD (ACE_LOCK, ace_mon, this->mutex_)); + + delete iterator_; + + for (ACE_Timer_Node_T *curr = this->head_->get_next (); + curr != this->head_; + ) + { + ACE_Timer_Node_T *next = curr->get_next (); + this->upcall_functor ().deletion (*this, + next->get_type (), + next->get_act ()); + this->free_node (curr); + curr = next; + } + + // delete the dummy node + delete this->head_; +} + +template void +ACE_Timer_List_T::dump (void) const +{ + ACE_TRACE ("ACE_Timer_List_T::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + size_t count = 0; + + for (ACE_Timer_Node_T *curr = this->head_->get_next (); + curr != this->head_; + curr = curr->get_next ()) + count++; + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nsize_ = %d"), count)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ntimer_id_ = %d"), this->timer_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + + +// Reschedule a periodic timer. This function must be called with the +// lock held. + +template void +ACE_Timer_List_T::reschedule (ACE_Timer_Node_T *expired) +{ + ACE_TRACE ("ACE_Timer_List_T::reschedule"); + + ACE_Timer_Node_T *after = this->head_->get_next (); + + // Locate the proper position in the queue. + + while (after != this->head_ + && expired->get_timer_value () > after->get_timer_value ()) + after = after->get_next (); + + expired->set_next (after); + expired->set_prev (after->get_prev ()); + after->get_prev ()->set_next (expired); + after->set_prev (expired); +} + + +// Insert a new handler that expires at time future_time; if interval +// is > 0, the handler will be reinvoked periodically. + +template long +ACE_Timer_List_T::schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &future_time, + const ACE_Time_Value &interval) +{ + ACE_TRACE ("ACE_Timer_List_T::schedule"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Place in the middle of the list where it belongs (i.e., sorted in + // ascending order of absolute time to expire). + ACE_Timer_Node_T *after = this->head_->get_next (); + + while (after != this->head_ + && future_time > after->get_timer_value ()) + after = after->get_next (); + + ACE_Timer_Node_T *temp = this->alloc_node (); + + temp->set (type, + act, + future_time, + interval, + after->get_prev (), + after, + (long) temp); + + after->get_prev ()->set_next (temp); + after->set_prev (temp); + + return ACE_reinterpret_cast (long, temp); +} + +// Locate and update the inteval on the timer_id + +template int +ACE_Timer_List_T::reset_interval (long timer_id, + const ACE_Time_Value &interval) +{ + ACE_TRACE ("ACE_Timer_List_T::reset_interval"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Make sure we are getting a valid , not an error + // returned by . + if (timer_id == -1) + return -1; + + ACE_Timer_Node_T *node = + ACE_reinterpret_cast (ACE_Timer_Node_T *, + timer_id); + + node->set_interval (interval); + + return 0; +} + +// Locate and remove the single with a value of +// from the timer queue. + +template int +ACE_Timer_List_T::cancel (long timer_id, + const void **act, + int dont_call) +{ + ACE_TRACE ("ACE_Timer_List_T::cancel"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Make sure we are getting a valid , not an error + // returned by schedule () + if (timer_id == -1) + return 0; + + ACE_Timer_Node_T *node = + ACE_reinterpret_cast (ACE_Timer_Node_T *, + timer_id); + + // Check to see if the node looks like a true ACE_Timer_Node_T + if (timer_id == node->get_timer_id ()) + { + node->get_next ()->set_prev (node->get_prev ()); + node->get_prev ()->set_next (node->get_next ()); + + if (act != 0) + *act = node->get_act (); + + if (dont_call == 0) + this->upcall_functor ().cancellation (*this, + node->get_type ()); + this->free_node (node); + return 1; + } + + // Wasn't valid + return 0; +} + + +// Locate and remove all values of from the timer queue. + +template int +ACE_Timer_List_T::cancel (const TYPE &type, + int dont_call) +{ + ACE_TRACE ("ACE_Timer_List_T::cancel"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + int number_of_cancellations = 0; + + for (ACE_Timer_Node_T *curr = this->head_->get_next (); + curr != this->head_; + ) + { + if (curr->get_type () == type) + { + number_of_cancellations++; + + curr->get_prev ()->set_next (curr->get_next ()); + curr->get_next ()->set_prev (curr->get_prev ()); + ACE_Timer_Node_T *temp = curr; + curr = curr->get_next (); + this->free_node (temp); + } + else + curr = curr->get_next (); + } + + if (dont_call == 0) + this->upcall_functor ().cancellation (*this, type); + + return number_of_cancellations; +} + +// Reads the first node on the list and returns it. + +template ACE_Timer_Node_T * +ACE_Timer_List_T::get_first (void) +{ + ACE_TRACE ("ACE_Timer_List_T::get_first"); + + return this->head_->get_next (); +} + +// Removes the first node on the list and returns it. + +template ACE_Timer_Node_T * +ACE_Timer_List_T::remove_first (void) +{ + ACE_TRACE ("ACE_Timer_List_T::remove_first"); + + // remove the node and fix the pointers + ACE_Timer_Node_T *temp = this->head_->get_next (); + this->head_->set_next (temp->get_next ()); + temp->get_next ()->set_prev (this->head_); + + return temp; +} + +#endif /* ACE_TIMER_LIST_T_C */ diff --git a/ace/Timer/Timer_List_T.h b/ace/Timer/Timer_List_T.h new file mode 100644 index 00000000000..a863ce39a3c --- /dev/null +++ b/ace/Timer/Timer_List_T.h @@ -0,0 +1,217 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_List_T.h + * + * $Id$ + * + * @author Douglas C. Schmidt + */ +//============================================================================= + +#ifndef ACE_TIMER_LIST_T_H +#define ACE_TIMER_LIST_T_H +#include "ace/pre.h" + +#include "ace/Timer_Queue_T.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Forward declaration. +template +class ACE_Timer_List_T; + +/** + * @class ACE_Timer_List_Iterator_T + * + * @brief Iterates over an . + * + * This is a generic iterator that can be used to visit every + * node of a timer queue. + */ +template +class ACE_Timer_List_Iterator_T : public ACE_Timer_Queue_Iterator_T +{ +public: + /// Constructor. + ACE_Timer_List_Iterator_T (ACE_Timer_List_T &); + + /// Destructor. + ~ACE_Timer_List_Iterator_T (void); + + /// Positions the iterator at the earliest node in the Timer Queue + virtual void first (void); + + /// Positions the iterator at the next node in the Timer Queue + virtual void next (void); + + /// Returns true when there are no more nodes in the sequence + virtual int isdone (void) const; + + /// Returns the node at the current position in the sequence + virtual ACE_Timer_Node_T *item (void); + +protected: + /// Pointer to the that we are iterating over. + ACE_Timer_List_T &timer_list_; + + ACE_Timer_Node_T *position_; +}; + +/** + * @class ACE_Timer_List_T + * + * @brief Provides a simple implementation of timers. + * + * This implementation uses a linked list of absolute times. + * Therefore, in the average case, scheduling and canceling + * timers is O(N) (where N is the total number of timers) and + * expiring timers is O(K) (where K is the total number of timers + * that are < the current time of day). + * More clever implementations could use a delta-list, a heap, + * or timing wheels, etc. For instance, + * is a subclass of that implements a + * heap-based callout queue. For most applications, the + * will perform substantially faster than the + * . + */ +template +class ACE_Timer_List_T : public ACE_Timer_Queue_T +{ +public: + /// Type of iterator + typedef ACE_Timer_List_Iterator_T LIST_ITERATOR; + + /// Iterator is a friend + friend class ACE_Timer_List_Iterator_T; + + /// Type inherited from + typedef ACE_Timer_Queue_T INHERITED; + + // = Initialization and termination methods. + /** + * Default constructor. is the instance of the + * FUNCTOR to be used by the list. If is 0, a + * default FUNCTOR will be created. the freelist of + * timer nodes. If 0, then a default freelist will be created. + */ + ACE_Timer_List_T (FUNCTOR *upcall_functor = 0, + ACE_Free_List > *freelist = 0); + + /// Destructor + virtual ~ACE_Timer_List_T (void); + + /// True if queue is empty, else false. + virtual int is_empty (void) const; + + /// Returns the time of the earlier node in the . + /// Must be called on a non-empty queue. + virtual const ACE_Time_Value &earliest_time (void) const; + + /** + * Schedule that will expire after amount of time, + * which is specified in absolute time. If it expires then is + * passed in as the value to the . If is != to + * then it is used to reschedule the + * automatically, using relative time to the current . + * This method returns a that uniquely identifies the the + * entry in an internal list. This can be used to + * cancel the timer before it expires. The cancellation ensures + * that are unique up to values of greater than 2 + * billion timers. As long as timers don't stay around longer than + * this there should be no problems with accidentally deleting the + * wrong timer. Returns -1 on failure (which is guaranteed never to + * be a valid ). + */ + virtual long schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval = ACE_Time_Value::zero); + + /** + * Resets the interval of the timer represented by to + * , which is specified in relative time to the current + * . If is equal to + * , the timer will become a non-rescheduling + * timer. Returns 0 if successful, -1 if not. + */ + virtual int reset_interval (long timer_id, + const ACE_Time_Value &interval); + + /** + * Cancel all timer associated with . If is 0 + * then the will be invoked. Returns number of timers + * cancelled. + */ + virtual int cancel (const TYPE &type, + int dont_call_handle_close = 1); + + /** + * Cancel the single timer that matches the value (which + * was returned from the method). If act is non-NULL + * then it will be set to point to the ``magic cookie'' argument + * passed in when the timer was registered. This makes it possible + * to free up the memory and avoid memory leaks. If is + * 0 then the will be invoked. Returns 1 if cancellation + * succeeded and 0 if the wasn't found. + */ + virtual int cancel (long timer_id, + const void **act = 0, + int dont_call_handle_close = 1); + + /// Returns a pointer to this 's iterator. + virtual ACE_Timer_Queue_Iterator_T &iter (void); + + /// Removes the earliest node from the queue and returns it + virtual ACE_Timer_Node_T *remove_first (void); + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Reschedule an "interval" . This should be private + /// but for now it needs to be public for + virtual void reschedule (ACE_Timer_Node_T *); + + /// Reads the earliest node from the queue and returns it. + virtual ACE_Timer_Node_T *get_first (void); + +protected: + /// Factory method that allocates a new node (uses operator new). +/* virtual ACE_Timer_Node_T *alloc_node (void); + + /// Factory method that frees a previously allocated node (uses + /// operator delete). + virtual void free_node (ACE_Timer_Node_T *); +*/ +private: + /// Pointer to linked list of . + ACE_Timer_Node_T *head_; + + /// Iterator used to expire timers. + LIST_ITERATOR *iterator_; + + /** + * Keeps track of the timer id that uniquely identifies each timer. + * This id can be used to cancel a timer via the + * method. + */ + long timer_id_; + + // = Don't allow these operations for now. + ACE_UNIMPLEMENTED_FUNC (ACE_Timer_List_T (const ACE_Timer_List_T &)) + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Timer_List_T &)) +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) && !defined(ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_List_T.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE && !ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("Timer_List_T.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TIMER_LIST_T_H */ diff --git a/ace/Timer/Timer_Queue.cpp b/ace/Timer/Timer_Queue.cpp new file mode 100644 index 00000000000..cfcfcd13ae6 --- /dev/null +++ b/ace/Timer/Timer_Queue.cpp @@ -0,0 +1,60 @@ +// $Id$ + +#if !defined (ACE_TIMER_QUEUE_C) +#define ACE_TIMER_QUEUE_C + +#include "ace/Containers.h" +#include "ace/Timer_Queue.h" + +ACE_RCSID(ace, Timer_Queue, "$Id$") + +#if defined (ACE_HAS_BROKEN_HPUX_TEMPLATES) +#include "ace/Timer_Hash.h" +#include "ace/Timer_Queue_T.cpp" + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class + ACE_Timer_Queue_T< + ACE_Event_Handler*, + ACE_Timer_Hash_Upcall< + ACE_Event_Handler*, + ACE_Event_Handler_Handle_Timeout_Upcall, + ACE_Null_Mutex>, + ACE_Null_Mutex>; + +template class + ACE_Timer_Queue_Iterator_T< + ACE_Event_Handler*, + ACE_Timer_Hash_Upcall< + ACE_Event_Handler*, + ACE_Event_Handler_Handle_Timeout_Upcall, + ACE_Null_Mutex>, + ACE_Null_Mutex>; +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ +#endif /* ACE_HAS_BROKEN_HPUX_TEMPLATES */ + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +template class ACE_Unbounded_Set *>; +template class ACE_Node *>; +template class ACE_Unbounded_Set_Iterator *>; +template class ACE_Timer_Node_Dispatch_Info_T; +template class ACE_Timer_Node_T; +template class ACE_Timer_Queue_T, ACE_SYNCH_RECURSIVE_MUTEX>; +template class ACE_Timer_Queue_Iterator_T, ACE_SYNCH_RECURSIVE_MUTEX>; +template class ACE_Event_Handler_Handle_Timeout_Upcall; + +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) + +#pragma instantiate ACE_Unbounded_Set *> +#pragma instantiate ACE_Node *> +#pragma instantiate ACE_Unbounded_Set_Iterator *> +#pragma instantiate ACE_Timer_Node_Dispatch_Info_T +#pragma instantiate ACE_Timer_Node_T +#pragma instantiate ACE_Timer_Queue_T, ACE_SYNCH_RECURSIVE_MUTEX> +#pragma instantiate ACE_Timer_Queue_Iterator_T, ACE_SYNCH_RECURSIVE_MUTEX> +#pragma instantiate ACE_Event_Handler_Handle_Timeout_Upcall + +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + + +#endif /* ACE_TIMER_QUEUE_C */ diff --git a/ace/Timer/Timer_Queue.h b/ace/Timer/Timer_Queue.h new file mode 100644 index 00000000000..54a0a8f1137 --- /dev/null +++ b/ace/Timer/Timer_Queue.h @@ -0,0 +1,45 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_Queue.h + * + * $Id$ + * + * @author Douglas C. Schmidt and + * Irfan Pyarali . + */ +//============================================================================= + +#ifndef ACE_TIMER_QUEUE_H +#define ACE_TIMER_QUEUE_H +#include "ace/pre.h" + +#include "ace/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Timer_Queue_T.h" + +// The following typedef are here for ease of use and backward +// compatibility. +typedef ACE_Timer_Node_Dispatch_Info_T + ACE_Timer_Node_Dispatch_Info; + +typedef ACE_Timer_Node_T + ACE_Timer_Node; + +typedef ACE_Timer_Queue_T, + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Timer_Queue; + +typedef ACE_Timer_Queue_Iterator_T, + ACE_SYNCH_RECURSIVE_MUTEX> + ACE_Timer_Queue_Iterator; + +#include "ace/post.h" +#endif /* ACE_TIMER_QUEUE_H */ diff --git a/ace/Timer/Timer_Queue_Adapters.cpp b/ace/Timer/Timer_Queue_Adapters.cpp new file mode 100644 index 00000000000..237e7349cbc --- /dev/null +++ b/ace/Timer/Timer_Queue_Adapters.cpp @@ -0,0 +1,310 @@ +// $Id$ + +#include "ace/Timer_Queue_Adapters.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#ifndef ACE_TIMER_QUEUE_ADAPTERS_C +# define ACE_TIMER_QUEUE_ADAPTERS_C + +ACE_RCSID(ace, Timer_Queue_Adapters, "$Id$") + +# if !defined (__ACE_INLINE__) +# include "ace/Timer_Queue_Adapters.i" +# endif /* __ACE_INLINE__ */ + +template TQ & +ACE_Async_Timer_Queue_Adapter::timer_queue (void) +{ + return this->timer_queue_; +} + +template int +ACE_Async_Timer_Queue_Adapter::cancel (long timer_id, + const void **act) +{ + // Block designated signals. + ACE_Sig_Guard sg (&this->mask_); + ACE_UNUSED_ARG (sg); + + return this->timer_queue_.cancel (timer_id, act); +} + +template int +ACE_Async_Timer_Queue_Adapter::expire (void) +{ + // Block designated signals. + ACE_Sig_Guard sg (&this->mask_); + ACE_UNUSED_ARG (sg); + + return this->timer_queue_.expire (); +} + +template int +ACE_Async_Timer_Queue_Adapter::schedule_ualarm (void) +{ + ACE_Time_Value tv = this->timer_queue_.earliest_time () + - ACE_OS::gettimeofday (); + + // Beware of negative times and zero times (which cause problems for + // ). + if (tv < ACE_Time_Value::zero) + tv = ACE_Time_Value (0, 1); + + // @@ This code should be clever enough to avoid updating the + // if we haven't actually changed the earliest time. + // Schedule a new timer. + ACE_OS::ualarm (tv); + return 0; +} + +template long +ACE_Async_Timer_Queue_Adapter::schedule (ACE_Event_Handler *eh, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval) +{ + ACE_UNUSED_ARG (act); + ACE_UNUSED_ARG (interval); + + // Block designated signals. + ACE_Sig_Guard sg (&this->mask_); + ACE_UNUSED_ARG (sg); + + // @@ We still need to implement interval timers... + long tid = this->timer_queue_.schedule (eh, act, delay); + + if (tid == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("schedule_timer")), + -1); + + if (this->schedule_ualarm () == -1) + return 0; + + return tid; +} + +template +ACE_Async_Timer_Queue_Adapter::ACE_Async_Timer_Queue_Adapter (ACE_Sig_Set *mask) + // If == 0, block *all* signals when the SIGARLM handler is + // running, else just block those in the mask. + : mask_ (mask) +{ + // The following code is necessary to selectively "block" certain + // signals when SIGALRM is running. Also, we always restart system + // calls that are interrupted by the signals. + + ACE_Sig_Action sa ((ACE_SignalHandler) 0, + this->mask_, + SA_RESTART); + + if (this->sig_handler_.register_handler (SIGALRM, this, &sa) == -1) + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("register_handler"))); +} + +// This is the signal handler function for the asynchronous timer +// list. It gets invoked asynchronously when the SIGALRM signal +// occurs. + +template int +ACE_Async_Timer_Queue_Adapter::handle_signal (int signum, + siginfo_t *, + ucontext_t *) +{ + switch (signum) + { + case SIGALRM: + { + // Expire the pending timers. + + // @@ We need to figure out how to implement interval + // timers... + this->timer_queue_.expire (); + + // Only schedule a new timer if there is one in the list. + + // @@ This code should also become smarter to avoid + // unnecessary calls to ualarm(). + if (this->timer_queue_.is_empty () == 0) + return this->schedule_ualarm (); + else + return 0; + /* NOTREACHED */ + } + default: + ACE_ERROR_RETURN ((LM_ERROR, + "unexpected signal %S\n", + signum), + -1); + /* NOTREACHED */ + } +} + +template +ACE_Thread_Timer_Queue_Adapter::ACE_Thread_Timer_Queue_Adapter (ACE_Thread_Manager *tm) + : ACE_Task_Base (tm), + condition_ (mutex_), + active_ (1), // Assume that we start in active mode. + thr_id_ (ACE_OS::NULL_thread) +{ +} + +template ACE_SYNCH_MUTEX & +ACE_Thread_Timer_Queue_Adapter::mutex (void) +{ + return this->mutex_; +} + +template long +ACE_Thread_Timer_Queue_Adapter::schedule + (ACE_Event_Handler* handler, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + long result = this->timer_queue_.schedule (handler, act, delay, interval); + this->condition_.signal (); + return result; +} + +template int +ACE_Thread_Timer_Queue_Adapter::cancel (long timer_id, + const void **act) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + int result = this->timer_queue_.cancel (timer_id, act); + condition_.signal (); + return result; +} + +template void +ACE_Thread_Timer_Queue_Adapter::deactivate (void) +{ + ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->mutex_); + + this->active_ = 0; + this->condition_.signal (); +} + +template int +ACE_Thread_Timer_Queue_Adapter::svc (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + this->thr_id_ = ACE_Thread::self (); + + // Thread cancellation point, if ACE supports it. + // + // Note: This call generates a warning under Solaris because the header + // file /usr/include/pthread.h redefines the routine argument. This + // is a bug in the Solaris header files and has nothing to do with + // ACE. +# if !defined (ACE_LACKS_PTHREAD_CANCEL) + ACE_PTHREAD_CLEANUP_PUSH (&this->condition_.mutex ()); +# endif /* ACE_LACKS_PTHREAD_CANCEL */ + + while (this->active_) + { +# if defined (ACE_HAS_DEFERRED_TIMER_COMMANDS) + // Temporarily suspend ownership of the timer queue mutex in + // order to dispatch deferred execution commands. These + // commands are to be treated as executing in a context + // "external" to the timer queue adapter, and thus must compete + // separately for this lock. + mutex_.release (); + this->dispatch_commands (); + + // Re-acquire ownership of the timer queue mutex in order to + // restore the "internal" timer queue adapter context + mutex_.acquire (); +# endif /* ACE_HAS_DEFERRED_TIMER_COMMANDS */ + + // If the queue is empty, sleep until there is a change on it. + if (this->timer_queue_.is_empty ()) + this->condition_.wait (); + else + { + // Compute the remaining time, being careful not to sleep + // for "negative" amounts of time. + ACE_Time_Value tv = this->timer_queue_.earliest_time (); + + // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("waiting until %u.%3.3u secs\n"), + // tv.sec(), tv.msec())); + this->condition_.wait (&tv); + } + + // Expire timers anyway, at worst this is a no-op. + this->timer_queue_.expire (); + } + + // Thread cancellation point, if ACE supports it. +# if !defined (ACE_LACKS_PTHREAD_CANCEL) + ACE_PTHREAD_CLEANUP_POP (0); +# endif /* ACE_LACKS_PTHREAD_CANCEL */ + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("terminating dispatching thread\n"))); + return 0; +} + + +# if defined (ACE_HAS_DEFERRED_TIMER_COMMANDS) + +// Enqueues a command object for execution just before waiting on the next +// timer event. This allows deferred execution of commands that cannot +// be performed in the timer event handler context, such as registering +// or cancelling timers on platforms where the timer queue mutex is not +// recursive. + +template int +ACE_Thread_Timer_Queue_Adapter::enqueue_command (ACE_Command_Base *cmd, + COMMAND_ENQUEUE_POSITION pos) +{ + // Serialize access to the command queue. + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, + this->command_mutex_, -1); + + if (pos == ACE_Thread_Timer_Queue_Adapter::TAIL) + { + return command_queue_.enqueue_tail (cmd); + } + else + { + return command_queue_.enqueue_head (cmd); + } +} + + +// Dispatches all command objects enqueued in the most +// recent event handler context. + +template int +ACE_Thread_Timer_Queue_Adapter::dispatch_commands (void) +{ + // Serialize access to the command queue. + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, + this->command_mutex_, -1); + + // loop through the enqueued commands + ACE_Command_Base *cmd = 0; + while (command_queue_.dequeue_head (cmd) == 0) + if (cmd) + { + cmd->execute (); + delete cmd; + } + + return 0; +} + +# endif /* ACE_HAS_DEFERRED_TIMER_COMMANDS */ + +#endif /* ACE_TIMER_QUEUE_ADAPTERS_C*/ diff --git a/ace/Timer/Timer_Queue_Adapters.h b/ace/Timer/Timer_Queue_Adapters.h new file mode 100644 index 00000000000..ef05e7f79f4 --- /dev/null +++ b/ace/Timer/Timer_Queue_Adapters.h @@ -0,0 +1,230 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file Timer_Queue_Adapters.h + * + * $Id$ + * + * @author Douglas C. Schmidt and Carlos O'Ryan + */ +//============================================================================= + + +#ifndef ACE_TIMER_QUEUE_ADAPTERS_H +#define ACE_TIMER_QUEUE_ADAPTERS_H +#include "ace/pre.h" + +#include "ace/Task.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Signal.h" + +/** + * @class ACE_Async_Timer_Queue_Adapter + * + * @brief Adapts a to be run asynchronously. + * + * This implementation uses the call, which generates + * the SIGARLM signal that is caught by this class. + */ +template +class ACE_Export ACE_Async_Timer_Queue_Adapter : public ACE_Event_Handler +{ +public: + typedef TQ TIMER_QUEUE; + + /// Constructor + /** + * Register the SIGALRM handler. If == 0 then block all + * signals when is run. Otherwise, just block the signals + * indicated in . + */ + ACE_Async_Timer_Queue_Adapter (ACE_Sig_Set *mask = 0); + + /// Schedule the timer according to the semantics of the + /// . + /** + * Tthis timer gets dispatched via a signal, rather than by a user + * calling . Note that interval timers are not implemented + * yet. + */ + long schedule (ACE_Event_Handler *type, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval = ACE_Time_Value::zero); + + /// Cancel the and pass back the if an address is + /// passed in. + int cancel (long timer_id, const void **act = 0); + + /// Dispatch all timers whose values are <= . Returns the + /// number of timers canceled. + int expire (void); + + /// Access the underlying . + TQ &timer_queue (void); + +private: + /// Perform the logic to compute the new ualarm(2) setting. + virtual int schedule_ualarm (void); + + /// Called back by handler. + virtual int handle_signal (int signum, siginfo_t *, ucontext_t *); + + /// Handler for the signal, so that we can access our state + /// without requiring any global variables. + ACE_Sig_Handler sig_handler_; + + /// Implementation of the timer queue (e.g., , + /// , etc.). + TQ timer_queue_; + + /// Mask of signals to be blocked when we're servicing . + ACE_Sig_Set mask_; +}; + +/** + * @class ACE_Thread_Timer_Queue_Adapter + * + * @brief Adapts a Timer_Queue using a separate thread for dispatching. + * + * This implementation of a Timer_Queue uses a separate thread to + * dispatch the timers. The base queue need not be thread safe, + * this class takes all the necessary locks. + * + * @note This is a case were template parameters will be useful, but + * (IMHO) the effort and portability problems discourage their + * use. + * + */ +template +class ACE_Export ACE_Thread_Timer_Queue_Adapter : public ACE_Task_Base +{ +public: + + /// Trait for the underlying queue type. + typedef TQ TIMER_QUEUE; + +# if defined (ACE_HAS_DEFERRED_TIMER_COMMANDS) + + /// Typedef for the position at which to enqueue a deferred execution command. + enum COMMAND_ENQUEUE_POSITION {HEAD, TAIL}; + +# endif /* ACE_HAS_DEFERRED_TIMER_COMMANDS */ + + /// Creates the timer queue. Activation of the task is the user's + /// responsibility. + ACE_Thread_Timer_Queue_Adapter (ACE_Thread_Manager * = ACE_Thread_Manager::instance ()); + + /// Schedule the timer according to the semantics of the ; wakes + /// up the dispatching thread. + long schedule (ACE_Event_Handler *handler, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval = ACE_Time_Value::zero); + + /// Cancel the add return the parameter if an + /// address is passed in. Also wakes up the dispatching thread. + int cancel (long timer_id, const void **act = 0); + + /// Runs the dispatching thread. + virtual int svc (void); + + /// Inform the dispatching thread that it should terminate. + virtual void deactivate (void); + + /// Access the locking mechanism, useful for iteration. + ACE_SYNCH_MUTEX &mutex (void); + + /// Access the implementation queue, useful for iteration. + TQ &timer_queue (void); + + /// Return the thread id of our active object. + ACE_thread_t thr_id (void); + + /** + * We override the default method so that we can ensure + * that only a single thread is ever spawned. Otherwise, too many + * weird things can happen... + */ + virtual int activate (long flags = THR_NEW_LWP | THR_JOINABLE, + int n_threads = 1, + int force_active = 0, + long priority = ACE_DEFAULT_THREAD_PRIORITY, + int grp_id = -1, + ACE_Task_Base *task = 0, + ACE_hthread_t thread_handles[] = 0, + void *stack[] = 0, + size_t stack_size[] = 0, + ACE_thread_t thread_names[] = 0); + +# if defined (ACE_HAS_DEFERRED_TIMER_COMMANDS) + + /** + * Enqueues a command object for execution just before waiting on the next + * timer event. This allows deferred execution of commands that cannot + * be performed in the timer event handler context, such as registering + * or cancelling timers on platforms where the timer queue mutex is not + * recursive. + */ + int enqueue_command (ACE_Command_Base *command_, + COMMAND_ENQUEUE_POSITION pos = TAIL); + +# endif /* ACE_HAS_DEFERRED_TIMER_COMMANDS */ + +private: + +# if defined (ACE_HAS_DEFERRED_TIMER_COMMANDS) + + /// Dispatches all command objects enqueued in the most + /// recent event handler context. + int dispatch_commands (void); + + /// Queue of commands for deferred execution. + ACE_Unbounded_Queue command_queue_; + + /// The mutual exclusion mechanism for the command queue. + ACE_SYNCH_MUTEX command_mutex_; + +# endif /* ACE_HAS_DEFERRED_TIMER_COMMANDS */ + + /// The underlying Timer_Queue. + TQ timer_queue_; + + /** + * The dispatching thread sleeps on this condition while waiting to + * dispatch the next timer; it is used to wake it up if there is a + * change on the timer queue. + */ + ACE_SYNCH_CONDITION condition_; + + /// The mutual exclusion mechanism which is required to use the + /// . + ACE_SYNCH_MUTEX mutex_; + + /// When deactivate is called this variable turns to false and the + /// dispatching thread is signalled, to terminate its main loop. + int active_; + + /// Thread id of our active object task. + ACE_thread_t thr_id_; +}; + +#if defined (__ACE_INLINE__) +# include "ace/Timer_Queue_Adapters.i" +#endif /* __ACE_INLINE__ */ + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +# include "ace/Timer_Queue_Adapters.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +# pragma implementation ("Timer_Queue_Adapters.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#include "ace/post.h" +#endif /* ACE_TIMER_QUEUE_ADAPTERS_H */ diff --git a/ace/Timer/Timer_Queue_Adapters.i b/ace/Timer/Timer_Queue_Adapters.i new file mode 100644 index 00000000000..621c9c08ef8 --- /dev/null +++ b/ace/Timer/Timer_Queue_Adapters.i @@ -0,0 +1,37 @@ +/* -*- C++ -*- */ +// $Id$ + +template ACE_INLINE TQ & +ACE_Thread_Timer_Queue_Adapter::timer_queue (void) +{ + return this->timer_queue_; +} + +template ACE_INLINE ACE_thread_t +ACE_Thread_Timer_Queue_Adapter::thr_id (void) +{ + return this->thr_id_; +} + +template ACE_INLINE int +ACE_Thread_Timer_Queue_Adapter::activate (long flags, + int n_threads, + int force_active, + long priority, + int grp_id, + ACE_Task_Base *task, + ACE_hthread_t thread_handles[], + void *stack[], + size_t stack_size[], + ACE_thread_t thread_names[]) +{ + // Macros to avoid "warning: unused parameter" type warning. + ACE_UNUSED_ARG (n_threads); + ACE_UNUSED_ARG (force_active); + ACE_UNUSED_ARG (thread_handles); + + // Make sure that we only allow a single thread to be spawned for + // our adapter. Otherwise, too many weird things can happen. + return ACE_Task_Base::activate (flags, 1, 0, priority, grp_id, task, 0, + stack, stack_size, thread_names); +} diff --git a/ace/Timer/Timer_Queue_T.cpp b/ace/Timer/Timer_Queue_T.cpp new file mode 100644 index 00000000000..166e35c60e6 --- /dev/null +++ b/ace/Timer/Timer_Queue_T.cpp @@ -0,0 +1,354 @@ +// $Id$ + +#ifndef ACE_TIMER_QUEUE_T_C +#define ACE_TIMER_QUEUE_T_C + +#include "ace/Synch.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Signal.h" +#include "ace/Timer_Queue_T.h" +#include "ace/Log_Msg.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Timer_Queue_T.i" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, Timer_Queue_T, "$Id$") + +template void +ACE_Timer_Node_T::dump (void) const +{ + ACE_TRACE ("ACE_Timer_Node_T::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nact_ = %x"), this->act_)); + this->timer_value_.dump (); + this->interval_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nprev_ = %x"), this->prev_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nnext_ = %x"), this->next_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ntimer_id_ = %d\n"), this->timer_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template +ACE_Timer_Node_T::ACE_Timer_Node_T (void) +{ + ACE_TRACE ("ACE_Timer_Node_T::ACE_Timer_Node_T"); +} + +template +ACE_Timer_Node_T::~ACE_Timer_Node_T (void) +{ + ACE_TRACE ("ACE_Timer_Node_T::~ACE_Timer_Node_T"); +} + + + +template +ACE_Timer_Queue_Iterator_T::ACE_Timer_Queue_Iterator_T (void) +{ +} + +template +ACE_Timer_Queue_Iterator_T::~ACE_Timer_Queue_Iterator_T (void) +{ +} + +// Determines the minimum amount of time that the Reactor must wait +// before timing out. This is computed as the smaller of (1) the +// amount the caller requested when calling handle_events() and (2) +// the earliest time registered in the Timer Queue (if any). Must be +// called with an external lock held since it returns a pointer to a +// Time_Value type stored in the Timer_Queue type itself. If some +// external lock isn't held we'll have reentrancy problems! + +template ACE_Time_Value * +ACE_Timer_Queue_T::calculate_timeout (ACE_Time_Value *max_wait_time) +{ + ACE_TRACE ("ACE_Timer_Queue_T::calculate_timeout"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, max_wait_time)); + + if (this->is_empty ()) + // Nothing on the Timer_Queue, so use whatever the caller gave us. + return max_wait_time; + else + { + ACE_Time_Value cur_time = this->gettimeofday (); + + if (this->earliest_time () > cur_time) + { + // The earliest item on the Timer_Queue is still in the + // future. Therefore, use the smaller of (1) caller's wait + // time or (2) the delta time between now and the earliest + // time on the Timer_Queue. + + this->timeout_ = this->earliest_time () - cur_time; + if (max_wait_time == 0 || *max_wait_time > timeout_) + return &this->timeout_; + else + return max_wait_time; + } + else + { + // The earliest item on the Timer_Queue is now in the past. + // Therefore, we've got to "poll" the Reactor, i.e., it must + // just check the descriptors and then dispatch timers, etc. + this->timeout_ = ACE_Time_Value::zero; + return &this->timeout_; + } + } +} + +template ACE_Time_Value * +ACE_Timer_Queue_T::calculate_timeout (ACE_Time_Value *max_wait_time, + ACE_Time_Value *the_timeout) +{ + ACE_TRACE ("ACE_Timer_Queue_T::calculate_timeout"); + + if (the_timeout == 0) + return 0; + + if (this->is_empty ()) + { + // Nothing on the Timer_Queue, so use whatever the caller gave us. + if (max_wait_time) + *the_timeout = *max_wait_time; + else + return 0; + } + else + { + ACE_Time_Value cur_time = this->gettimeofday (); + + if (this->earliest_time () > cur_time) + { + // The earliest item on the Timer_Queue is still in the + // future. Therefore, use the smaller of (1) caller's wait + // time or (2) the delta time between now and the earliest + // time on the Timer_Queue. + + *the_timeout = this->earliest_time () - cur_time; + if (!(max_wait_time == 0 || *max_wait_time > *the_timeout)) + *the_timeout = *max_wait_time; + } + else + { + // The earliest item on the Timer_Queue is now in the past. + // Therefore, we've got to "poll" the Reactor, i.e., it must + // just check the descriptors and then dispatch timers, etc. + *the_timeout = ACE_Time_Value::zero; + } + } + return the_timeout; +} + +template void +ACE_Timer_Queue_T::dump (void) const +{ + ACE_TRACE ("ACE_Timer_Queue_T::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->timeout_.dump (); + this->timer_skew_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +template +ACE_Timer_Queue_T::ACE_Timer_Queue_T (FUNCTOR *upcall_functor, + ACE_Free_List > *freelist) + : gettimeofday_ (ACE_OS::gettimeofday), + delete_upcall_functor_ (upcall_functor == 0), + delete_free_list_ (freelist == 0), + timer_skew_ (0, ACE_TIMER_SKEW) +{ + ACE_TRACE ("ACE_Timer_Queue_T::ACE_Timer_Queue_T"); + + if (!freelist) + ACE_NEW (free_list_, + (ACE_Locked_Free_List,ACE_Null_Mutex>)); + else + free_list_ = freelist; + + if (!upcall_functor) + ACE_NEW (upcall_functor_, + FUNCTOR); + else + upcall_functor_ = upcall_functor; +} + +template +ACE_Timer_Queue_T::~ACE_Timer_Queue_T (void) +{ + ACE_TRACE ("ACE_Timer_Queue_T::~ACE_Timer_Queue_T"); + + // Cleanup the functor and free_list on the way out + if (this->delete_upcall_functor_) + delete this->upcall_functor_; + + if (this->delete_free_list_) + delete this->free_list_; +} + +template ACE_Timer_Node_T * +ACE_Timer_Queue_T::alloc_node (void) +{ + return this->free_list_->remove (); +} + +template void +ACE_Timer_Queue_T::free_node (ACE_Timer_Node_T *node) +{ + this->free_list_->add (node); +} + +template ACE_LOCK & +ACE_Timer_Queue_T::mutex (void) +{ + return this->mutex_; +} + + +// Run the method for all Timers whose values are <= +// . +template int +ACE_Timer_Queue_T::expire (const ACE_Time_Value &cur_time) +{ + ACE_TRACE ("ACE_Timer_Queue_T::expire"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Keep looping while there are timers remaining and the earliest + // timer is <= the passed in to the method. + + if (this->is_empty ()) + return 0; + + int number_of_timers_expired = 0; + int result = 0; + + ACE_Timer_Node_Dispatch_Info_T info; + + while ((result = this->dispatch_info_i (cur_time, + info)) != 0) + { + // call the functor + this->upcall (info.type_, info.act_, cur_time); + + number_of_timers_expired++; + + } + + ACE_UNUSED_ARG (result); + return number_of_timers_expired; +} + + +template int +ACE_Timer_Queue_T::dispatch_info_i (const ACE_Time_Value &cur_time, + ACE_Timer_Node_Dispatch_Info_T &info) +{ + ACE_TRACE ("ACE_Timer_Queue_T::dispatch_info_i"); + + if (this->is_empty ()) + return 0; + + ACE_Timer_Node_T *expired = 0; + + if (this->earliest_time () <= cur_time) + { + expired = this->remove_first (); + + // Get the dispatch info + expired->get_dispatch_info (info); + + // Check if this is an interval timer. + if (expired->get_interval () > ACE_Time_Value::zero) + { + // Make sure that we skip past values that have already + // "expired". + do + expired->set_timer_value (expired->get_timer_value () + expired->get_interval ()); + while (expired->get_timer_value () <= cur_time); + + // Since this is an interval timer, we need to reschedule + // it. + this->reschedule (expired); + } + else + { + // Call the factory method to free up the node. + this->free_node (expired); + } + + return 1; + } + + return 0; +} + + +template void +ACE_Timer_Queue_T::return_node (ACE_Timer_Node_T *node) +{ + ACE_MT (ACE_GUARD (ACE_LOCK, ace_mon, this->mutex_)); + this->free_node (node); +} + + +template +ACE_Event_Handler_Handle_Timeout_Upcall::ACE_Event_Handler_Handle_Timeout_Upcall (void) +{ +} + +template +ACE_Event_Handler_Handle_Timeout_Upcall::~ACE_Event_Handler_Handle_Timeout_Upcall (void) +{ +} + +template int +ACE_Event_Handler_Handle_Timeout_Upcall::timeout (ACE_Timer_Queue_T, + ACE_LOCK> &timer_queue, + ACE_Event_Handler *handler, + const void *act, + const ACE_Time_Value &cur_time) +{ + // Upcall to the s handle_timeout method. + if (handler->handle_timeout (cur_time, act) == -1) + timer_queue.cancel (handler, 0); // 0 means "call handle_close()". + + return 0; +} + +template int +ACE_Event_Handler_Handle_Timeout_Upcall::cancellation (ACE_Timer_Queue_T, + ACE_LOCK> &timer_queue, + ACE_Event_Handler *handler) +{ + ACE_UNUSED_ARG (timer_queue); + + // Upcall to the s handle_close method + handler->handle_close (ACE_INVALID_HANDLE, + ACE_Event_Handler::TIMER_MASK); + return 0; +} + +template int +ACE_Event_Handler_Handle_Timeout_Upcall::deletion (ACE_Timer_Queue_T, + ACE_LOCK> &timer_queue, + ACE_Event_Handler *handler, + const void *arg) +{ + ACE_UNUSED_ARG (timer_queue); + ACE_UNUSED_ARG (handler); + ACE_UNUSED_ARG (arg); + + // Does nothing + + return 0; +} + +#endif /* ACE_TIMER_QUEUE_T_C*/ diff --git a/ace/Timer/Timer_Queue_T.h b/ace/Timer/Timer_Queue_T.h new file mode 100644 index 00000000000..55735ace482 --- /dev/null +++ b/ace/Timer/Timer_Queue_T.h @@ -0,0 +1,498 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Timer_Queue_T.h + * + * $Id$ + * + * @author Doug Schmidt + * @author Irfan Pyarali and + * @author Darrell Brunsch + */ +//============================================================================= + +#ifndef ACE_TIMER_QUEUE_T_H +#define ACE_TIMER_QUEUE_T_H +#include "ace/pre.h" + +#include "ace/Free_List.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Test_and_Set.h" + +/** + * @class ACE_Timer_Node_Dispatch_Info_T + * + * @brief Maintains generated dispatch information for Timer nodes. + * + */ +template +class ACE_Timer_Node_Dispatch_Info_T +{ +public: + // The type of object held in the queue + TYPE type_; + + /// Asynchronous completion token associated with the timer. + const void *act_; +}; + +/** + * @class ACE_Timer_Node_T + * + * @brief Maintains the state associated with a Timer entry. + */ +template +class ACE_Timer_Node_T +{ +public: + /// Default constructor + ACE_Timer_Node_T (void); + + /// Dtor. + ~ACE_Timer_Node_T (void); + + /// Useful typedef .. + typedef ACE_Timer_Node_Dispatch_Info_T DISPATCH_INFO; + + /// singly linked list + void set (const TYPE &type, + const void *a, + const ACE_Time_Value &t, + const ACE_Time_Value &i, + ACE_Timer_Node_T *n, + long timer_id); + + /// doubly linked list version + void set (const TYPE &type, + const void *a, + const ACE_Time_Value &t, + const ACE_Time_Value &i, + ACE_Timer_Node_T *p, + ACE_Timer_Node_T *n, + long timer_id); + + // = Accessors + + /// Get the type. + TYPE &get_type (void); + + /// Set the type. + void set_type (TYPE &type); + + /// Get the asynchronous completion token. + const void *get_act (void); + + /// set the asynchronous completion token. + void set_act (void *act); + + /// get the timer value. + const ACE_Time_Value &get_timer_value (void) const; + + /// set the timer value. + void set_timer_value (const ACE_Time_Value &timer_value); + + /// get the timer interval. + const ACE_Time_Value &get_interval (void) const; + + /// Set the timer interval. + void set_interval (const ACE_Time_Value &interval); + + /// get the previous pointer. + ACE_Timer_Node_T *get_prev (void); + + /// set the previous pointer. + void set_prev (ACE_Timer_Node_T *prev); + + /// get the next pointer. + ACE_Timer_Node_T *get_next (void); + + /// set the next pointer. + void set_next (ACE_Timer_Node_T *next); + + /// get the timer_id. + long get_timer_id (void) const; + + /// set the timer_id. + void set_timer_id (long timer_id); + + /// Get the dispatch info. The dispatch information is got + /// through . This form helps us in preventing allocation and + /// deleting data along the criticl path. + /// @@TODO: We may want to have a copying version too, so that our + /// interface will be complete.. + void get_dispatch_info (ACE_Timer_Node_Dispatch_Info_T &info); + + + /// Dump the state of an TYPE. + void dump (void) const; + +private: + /// Type of object stored in the Queue + TYPE type_; + + /// Asynchronous completion token associated with the timer. + const void *act_; + + /// Time until the timer expires. + ACE_Time_Value timer_value_; + + /// If this is a periodic timer this holds the time until the next + /// timeout. + ACE_Time_Value interval_; + + /// Pointer to previous timer. + ACE_Timer_Node_T *prev_; + + /// Pointer to next timer. + ACE_Timer_Node_T *next_; + + /// Id of this timer (used to cancel timers before they expire). + long timer_id_; +}; + +/** + * @class ACE_Timer_Queue_Iterator_T + * + * @brief Generic interface for iterating over a subclass of + * . + * + * This is a generic iterator that can be used to visit every + * node of a timer queue. Be aware that it isn't guaranteed + * that the transversal will be in order of timeout values. + */ +template +class ACE_Timer_Queue_Iterator_T +{ +public: + // = Initialization and termination methods. + /// Constructor. + ACE_Timer_Queue_Iterator_T (void); + + /// Destructor. + virtual ~ACE_Timer_Queue_Iterator_T (void); + + /// Positions the iterator at the earliest node in the Timer Queue + virtual void first (void) = 0; + + /// Positions the iterator at the next node in the Timer Queue + virtual void next (void) = 0; + + /// Returns true when there are no more nodes in the sequence + virtual int isdone (void) const = 0; + + /// Returns the node at the current position in the sequence + virtual ACE_Timer_Node_T *item (void) = 0; +}; + +/** + * @class ACE_Timer_Queue_T + * + * @brief Provides an interface to timers. + * + * This is an abstract base class that provides hook for + * implementing specialized policies such as + * and . + */ +template +class ACE_Timer_Queue_T +{ +public: + /// Type of Iterator. + typedef ACE_Timer_Queue_Iterator_T ITERATOR; + + // = Initialization and termination methods. + /** + * Default constructor. is the instance of the + * FUNCTOR to be used by the queue. If is 0, Timer + * Queue will create a default FUNCTOR. the freelist of + * timer nodes. If 0, then a default freelist will be created. + */ + ACE_Timer_Queue_T (FUNCTOR *upcall_functor = 0, + ACE_Free_List > *freelist = 0); + + /// Destructor - make virtual for proper destruction of inherited + /// classes. + virtual ~ACE_Timer_Queue_T (void); + + /// True if queue is empty, else false. + virtual int is_empty (void) const = 0; + + /// Returns the time of the earlier node in the Timer_Queue. Must + /// be called on a non-empty queue. + virtual const ACE_Time_Value &earliest_time (void) const = 0; + + /** + * Schedule that will expire after amount of time, + * which is specified in absolute time. If it expires then is + * passed in as the value to the . If is != to + * then it is used to reschedule the + * automatically, using relative time to the current . + * This method returns a that uniquely identifies the the + * entry in an internal list. This can be used to + * cancel the timer before it expires. The cancellation ensures + * that are unique up to values of greater than 2 + * billion timers. As long as timers don't stay around longer than + * this there should be no problems with accidentally deleting the + * wrong timer. Returns -1 on failure (which is guaranteed never to + * be a valid ). + */ + virtual long schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &delay, + const ACE_Time_Value &interval = ACE_Time_Value::zero) = 0; + + /** + * Resets the interval of the timer represented by to + * , which is specified in relative time to the current + * . If is equal to + * , the timer will become a non-rescheduling + * timer. Returns 0 if successful, -1 if not. + */ + virtual int reset_interval (long timer_id, + const ACE_Time_Value &interval) = 0; + + /** + * Cancel all timer associated with . If + * is 0 then the will be invoked, + * which typically invokes the hook. Returns number + * of timers cancelled. + */ + virtual int cancel (const TYPE &type, + int dont_call_handle_close = 1) = 0; + + /** + * Cancel the single timer that matches the value (which + * was returned from the method). If act is non-NULL + * then it will be set to point to the ``magic cookie'' argument + * passed in when the timer was registered. This makes it possible + * to free up the memory and avoid memory leaks. If + * is 0 then the will be invoked, + * which typically calls the hook. Returns 1 if + * cancellation succeeded and 0 if the wasn't found. + */ + virtual int cancel (long timer_id, + const void **act = 0, + int dont_call_handle_close = 1) = 0; + + /** + * Run the for all timers whose values are <= . + * This does not account for . Returns the number of + * timers canceled. + */ + virtual int expire (const ACE_Time_Value ¤t_time); + + /** + * Get the dispatch information for a timer whose value is <= . + * This does not account for . Returns 1 if + * there is a node whose value <= else returns a 0. + * + */ + int dispatch_info (const ACE_Time_Value ¤t_time, + ACE_Timer_Node_Dispatch_Info_T &info); + + /** + * Run the for all timers whose values are <= + * . Also accounts for . + * + * Depending on the resolution of the underlying OS the system calls + * like select()/poll() might return at time different than that is + * specified in the timeout. Suppose the OS guarantees a resolution of t ms. + * The timeline will look like + * + * A B + * | | + * V V + * |-------------|-------------|-------------|-------------| + * t t t t t + * + * + * If you specify a timeout value of A, then the timeout will not occur + * at A but at the next interval of the timer, which is later than + * that is expected. Similarly, if your timeout value is equal to B, + * then the timeout will occur at interval after B. Now depending upon the + * resolution of your timeouts and the accuracy of the timeouts + * needed for your application, you should set the value of + * . In the above case, if you want the timeout A to fire + * no later than A, then you should specify your to be + * A % t. + * + * The timeout value should be specified via the macro ACE_TIMER_SKEW + * in your config.h file. The default value is zero. + * + * Things get interesting if the t before the timeout value B is zero + * i.e your timeout is less than the interval. In that case, you are + * almost sure of not getting the desired timeout behaviour. Maybe you + * should look for a better OS :-) + * + * Returns the number of timers canceled. + */ + + /* virtual */ int expire (void); + + /** + * Returns the current time of day. This method allows different + * implementations of the timer queue to use special high resolution + * timers. + */ + /* virtual */ ACE_Time_Value gettimeofday (void); + + /// Allows applications to control how the timer queue gets the time + /// of day. + void gettimeofday (ACE_Time_Value (*gettimeofday)(void)); + + /// Determine the next event to timeout. Returns if there are + /// no pending timers or if all pending timers are longer than max. + virtual ACE_Time_Value *calculate_timeout (ACE_Time_Value *max); + + /** + * Determine the next event to timeout. Returns if there are + * no pending timers or if all pending timers are longer than max. + * should be a pointer to storage for the timeout value, + * and this value is also returned. + */ + virtual ACE_Time_Value *calculate_timeout (ACE_Time_Value *max, + ACE_Time_Value *the_timeout); + + // = Set/get the timer skew for the Timer_Queue. + void timer_skew (const ACE_Time_Value &skew); + const ACE_Time_Value &timer_skew (void) const; + + /// Synchronization variable used by the queue + ACE_LOCK &mutex (void); + + /// Accessor to the upcall functor + FUNCTOR &upcall_functor (void); + + /// Returns a pointer to this 's iterator. + virtual ITERATOR &iter (void) = 0; + + /// Removes the earliest node from the queue and returns it + virtual ACE_Timer_Node_T *remove_first (void) = 0; + + /// Dump the state of a object. + virtual void dump (void) const; + + /// Reads the earliest node from the queue and returns it. + virtual ACE_Timer_Node_T *get_first (void) = 0; + + /// Method used to return a timer node to the queue's ownership + /// after it is returned by a method like . + virtual void return_node (ACE_Timer_Node_T *); + + + /// This method will call the with the , and + ///