00001 // Filename: clientBase.cxx 00002 // Created by: jason (04Aug00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 00016 #include "clientBase.h" 00017 #include "config_device.h" 00018 00019 TypeHandle ClientBase::_type_handle; 00020 00021 //////////////////////////////////////////////////////////////////// 00022 // Function: ClientBase::constructor 00023 // Access: Protected 00024 // Description: 00025 //////////////////////////////////////////////////////////////////// 00026 ClientBase:: 00027 ClientBase() { 00028 _forked = false; 00029 _last_poll_time = 0.0f; 00030 _last_poll_frame = 0; 00031 _cs = CS_default; 00032 00033 #ifdef OLD_HAVE_IPC 00034 _client_thread = (thread *)NULL; 00035 _shutdown = false; 00036 #endif 00037 } 00038 00039 00040 //////////////////////////////////////////////////////////////////// 00041 // Function: ClientBase::destructor 00042 // Access: Public 00043 // Description: 00044 //////////////////////////////////////////////////////////////////// 00045 ClientBase:: 00046 ~ClientBase() { 00047 // We have to disconnect all of our devices before destructing. 00048 Devices::iterator di; 00049 Devices devices_copy = _devices; 00050 for (di = devices_copy.begin(); di != devices_copy.end(); ++di) { 00051 DevicesByName &dbn = (*di).second; 00052 DevicesByName::iterator dbni; 00053 for (dbni = dbn.begin(); dbni != dbn.end(); ++dbni) { 00054 ClientDevice *device = (*dbni).second; 00055 device->disconnect(); 00056 } 00057 } 00058 00059 #ifdef OLD_HAVE_IPC 00060 if (_forked) { 00061 _shutdown = true; 00062 00063 // Join the loader thread - calling process blocks until the loader 00064 // thread returns. 00065 void *ret; 00066 _client_thread->join(&ret); 00067 } 00068 #endif 00069 } 00070 00071 //////////////////////////////////////////////////////////////////// 00072 // Function: ClientBase::fork_asynchronous_thread 00073 // Access: Public 00074 // Description: Forks a separate thread to do all the polling of 00075 // connected devices. The forked thread will poll after 00076 // every poll_time seconds has elapsed. Returns true if 00077 // the fork was successful, or false otherwise (for 00078 // instance, because we were already forked, or because 00079 // asynchronous threads are disabled). 00080 //////////////////////////////////////////////////////////////////// 00081 bool ClientBase:: 00082 fork_asynchronous_thread(double poll_time) { 00083 #ifdef OLD_HAVE_IPC 00084 if (_forked) { 00085 device_cat.error() 00086 << "Attempt to fork client thread twice.\n"; 00087 return false; 00088 } 00089 00090 if (asynchronous_clients) { 00091 _sleep_time = (int)(1000000 * poll_time); 00092 00093 _client_thread = thread::create(&st_callback, this); 00094 _forked = true; 00095 if (device_cat.is_debug()) { 00096 device_cat.debug() 00097 << "fork_asynchronous_thread() - forking client thread" 00098 << endl; 00099 } 00100 return true; 00101 } 00102 #endif 00103 00104 return false; 00105 } 00106 00107 //////////////////////////////////////////////////////////////////// 00108 // Function: ClientBase::get_device 00109 // Access: Public 00110 // Description: Returns a ClientDevice pointer that corresponds to 00111 // the named device of the indicated device type. The 00112 // device_type should be one of ClientTrackerDevice, 00113 // ClientAnalogDevice, etc.; the device_name is 00114 // implementation defined. 00115 // 00116 // Normally, the user does not need to call this 00117 // function directly; it is called automatically by 00118 // creating a TrackerNode or AnalogNode or some such 00119 // data graph node. 00120 // 00121 // The return value is the pointer to the created device 00122 // (which might be the same pointer returned by a 00123 // previous call to this function with the same 00124 // parameters). When the pointer destructs (i.e. its 00125 // reference count reaches zero) it will automatically 00126 // be disconnected. 00127 // 00128 // If the named device does not exist or cannot be 00129 // connected for some reason, NULL is returned. 00130 //////////////////////////////////////////////////////////////////// 00131 PT(ClientDevice) ClientBase:: 00132 get_device(TypeHandle device_type, const string &device_name) { 00133 DevicesByName &dbn = _devices[device_type]; 00134 00135 DevicesByName::iterator dbni; 00136 dbni = dbn.find(device_name); 00137 if (dbni != dbn.end()) { 00138 // This device was previously connected. Return it again. 00139 return (*dbni).second; 00140 } 00141 00142 // We need to create a new device for this name. 00143 PT(ClientDevice) device = make_device(device_type, device_name); 00144 00145 if (device != (ClientDevice *)NULL) { 00146 dbn.insert(DevicesByName::value_type(device_name, device)); 00147 device->_is_connected = true; 00148 } 00149 00150 return device; 00151 } 00152 00153 //////////////////////////////////////////////////////////////////// 00154 // Function: ClientBase::disconnect_device 00155 // Access: Protected, Virtual 00156 // Description: Removes the device, which is presumably about to 00157 // destruct, from the list of connected devices, and 00158 // frees any data required to support it. This device 00159 // will no longer receive automatic updates with each 00160 // poll. 00161 // 00162 // The return value is true if the device was 00163 // disconnected, or false if it was unknown (e.g. it was 00164 // disconnected previously). 00165 //////////////////////////////////////////////////////////////////// 00166 bool ClientBase:: 00167 disconnect_device(TypeHandle device_type, const string &device_name, 00168 ClientDevice *device) { 00169 DevicesByName &dbn = _devices[device_type]; 00170 00171 DevicesByName::iterator dbni; 00172 dbni = dbn.find(device_name); 00173 if (dbni != dbn.end()) { 00174 if ((*dbni).second == device) { 00175 // We found it! 00176 dbn.erase(dbni); 00177 return true; 00178 } 00179 } 00180 00181 // The device was unknown. 00182 return false; 00183 } 00184 00185 //////////////////////////////////////////////////////////////////// 00186 // Function: ClientBase::do_poll 00187 // Access: Protected, Virtual 00188 // Description: Implements the polling and updating of connected 00189 // devices, if the ClientBase requires this. This may 00190 // be called in a sub-thread if 00191 // fork_asynchronous_thread() was called; otherwise, it 00192 // will be called once per frame. 00193 //////////////////////////////////////////////////////////////////// 00194 void ClientBase:: 00195 do_poll() { 00196 ClockObject *global_clock = ClockObject::get_global_clock(); 00197 _last_poll_frame = global_clock->get_frame_count(); 00198 _last_poll_time = global_clock->get_frame_time(); 00199 } 00200 00201 #ifdef OLD_HAVE_IPC 00202 //////////////////////////////////////////////////////////////////// 00203 // Function: ClientBase::st_callback 00204 // Access: Private, static 00205 // Description: Call back function for thread (if thread has been 00206 // spawned). A call back function must be static, so 00207 // this merely calls the non-static member callback In 00208 // addition, the function has a void* return type even 00209 // though we don't actually return anything. This is 00210 // necessary because ipc assumes a function that does 00211 // not return anything indicates that the associated 00212 // thread should be created as unjoinable (detached). 00213 //////////////////////////////////////////////////////////////////// 00214 void *ClientBase:: 00215 st_callback(void *arg) { 00216 nassertr(arg != NULL, NULL); 00217 ((ClientBase *)arg)->callback(); 00218 return NULL; 00219 } 00220 00221 //////////////////////////////////////////////////////////////////// 00222 // Function: ClientBase::callback 00223 // Access: Private 00224 // Description: This is the main body of the sub-thread. It sleeps 00225 // a certain time and then polls all devices currently 00226 // being watched 00227 //////////////////////////////////////////////////////////////////// 00228 void ClientBase:: 00229 callback() { 00230 while (true) { 00231 if (_shutdown) { 00232 break; 00233 } 00234 do_poll(); 00235 ipc_traits::sleep(0, _sleep_time); 00236 } 00237 } 00238 #endif // OLD_HAVE_IPC