jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
22KB

  1. /*
  2. Copyright (C) 2008 Romain Moret at Grame
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #if defined(HAVE_CONFIG_H)
  16. #include "config.h"
  17. #endif
  18. #include "JackNetManager.h"
  19. #define DEFAULT_MULTICAST_IP "225.3.19.154"
  20. #define DEFAULT_PORT 19000
  21. using namespace std;
  22. namespace Jack
  23. {
  24. //JackNetMaster******************************************************************************************************
  25. JackNetMaster::JackNetMaster ( JackNetSocket& socket, session_params_t& params, const char* multicast_ip )
  26. : JackNetMasterInterface ( params, socket, multicast_ip )
  27. {
  28. jack_log ( "JackNetMaster::JackNetMaster" );
  29. //settings
  30. fClientName = const_cast<char*> ( fParams.fName );
  31. fJackClient = NULL;
  32. fSyncState = 1;
  33. uint port_index;
  34. //jack audio ports
  35. fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels];
  36. for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ )
  37. fAudioCapturePorts[port_index] = NULL;
  38. fAudioPlaybackPorts = new jack_port_t* [fParams.fReturnAudioChannels];
  39. for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ )
  40. fAudioPlaybackPorts[port_index] = NULL;
  41. //jack midi ports
  42. fMidiCapturePorts = new jack_port_t* [fParams.fSendMidiChannels];
  43. for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ )
  44. fMidiCapturePorts[port_index] = NULL;
  45. fMidiPlaybackPorts = new jack_port_t* [fParams.fReturnMidiChannels];
  46. for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ )
  47. fMidiPlaybackPorts[port_index] = NULL;
  48. SetParams();
  49. //monitor
  50. #ifdef JACK_MONITOR
  51. fPeriodUsecs = ( int ) ( 1000000.f * ( ( float ) fParams.fPeriodSize / ( float ) fParams.fSampleRate ) );
  52. string plot_name;
  53. plot_name = string ( fParams.fName );
  54. plot_name += string ( "_master" );
  55. plot_name += string ( ( fParams.fSlaveSyncMode ) ? "_sync" : "_async" );
  56. switch ( fParams.fNetworkMode )
  57. {
  58. case 's' :
  59. plot_name += string ( "_slow" );
  60. break;
  61. case 'n' :
  62. plot_name += string ( "_normal" );
  63. break;
  64. case 'f' :
  65. plot_name += string ( "_fast" );
  66. break;
  67. }
  68. fNetTimeMon = new JackGnuPlotMonitor<float> ( 128, 4, plot_name );
  69. string net_time_mon_fields[] =
  70. {
  71. string ( "sync send" ),
  72. string ( "end of send" ),
  73. string ( "sync recv" ),
  74. string ( "end of cycle" )
  75. };
  76. string net_time_mon_options[] =
  77. {
  78. string ( "set xlabel \"audio cycles\"" ),
  79. string ( "set ylabel \"% of audio cycle\"" )
  80. };
  81. fNetTimeMon->SetPlotFile ( net_time_mon_options, 2, net_time_mon_fields, 4 );
  82. #endif
  83. }
  84. JackNetMaster::~JackNetMaster()
  85. {
  86. jack_log ( "JackNetMaster::~JackNetMaster, ID %u.", fParams.fID );
  87. if ( fJackClient )
  88. {
  89. jack_deactivate ( fJackClient );
  90. FreePorts();
  91. jack_client_close ( fJackClient );
  92. }
  93. delete[] fAudioCapturePorts;
  94. delete[] fAudioPlaybackPorts;
  95. delete[] fMidiCapturePorts;
  96. delete[] fMidiPlaybackPorts;
  97. #ifdef JACK_MONITOR
  98. fNetTimeMon->Save();
  99. delete fNetTimeMon;
  100. #endif
  101. }
  102. bool JackNetMaster::Init()
  103. {
  104. //network init
  105. if ( !JackNetMasterInterface::Init() )
  106. return false;
  107. //jack client and process
  108. jack_status_t status;
  109. if ( ( fJackClient = jack_client_open ( fClientName, JackNullOption, &status, NULL ) ) == NULL )
  110. {
  111. jack_error ( "Can't open a new jack client." );
  112. return false;
  113. }
  114. jack_set_process_callback ( fJackClient, SetProcess, this );
  115. if ( AllocPorts() != 0 )
  116. {
  117. jack_error ( "Can't allocate jack ports." );
  118. goto fail;
  119. }
  120. //process can now run
  121. fRunning = true;
  122. //finally activate jack client
  123. if ( jack_activate ( fJackClient ) != 0 )
  124. {
  125. jack_error ( "Can't activate jack client." );
  126. goto fail;
  127. }
  128. jack_info ( "NetJack new master started." );
  129. return true;
  130. fail:
  131. FreePorts();
  132. jack_client_close ( fJackClient );
  133. fJackClient = NULL;
  134. return false;
  135. }
  136. int JackNetMaster::AllocPorts()
  137. {
  138. jack_log ( "JackNetMaster::AllocPorts" );
  139. uint i;
  140. char name[24];
  141. jack_nframes_t port_latency = jack_get_buffer_size ( fJackClient );
  142. unsigned long port_flags;
  143. //audio
  144. port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal;
  145. for ( i = 0; i < fParams.fSendAudioChannels; i++ )
  146. {
  147. sprintf ( name, "to_slave_%d", i+1 );
  148. if ( ( fAudioCapturePorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0 ) ) == NULL )
  149. return -1;
  150. jack_port_set_latency ( fAudioCapturePorts[i], 0 );
  151. }
  152. port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal;
  153. for ( i = 0; i < fParams.fReturnAudioChannels; i++ )
  154. {
  155. sprintf ( name, "from_slave_%d", i+1 );
  156. if ( ( fAudioPlaybackPorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0 ) ) == NULL )
  157. return -1;
  158. jack_port_set_latency ( fAudioPlaybackPorts[i], ( fParams.fNetworkMode == 'f' ) ? 0 : port_latency + ( fParams.fSlaveSyncMode ) ? 0 : port_latency );
  159. }
  160. //midi
  161. port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal;
  162. for ( i = 0; i < fParams.fSendMidiChannels; i++ )
  163. {
  164. sprintf ( name, "midi_to_slave_%d", i+1 );
  165. if ( ( fMidiCapturePorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_MIDI_TYPE, port_flags, 0 ) ) == NULL )
  166. return -1;
  167. jack_port_set_latency ( fMidiCapturePorts[i], 0 );
  168. }
  169. port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal;
  170. for ( i = 0; i < fParams.fReturnMidiChannels; i++ )
  171. {
  172. sprintf ( name, "midi_from_slave_%d", i+1 );
  173. if ( ( fMidiPlaybackPorts[i] = jack_port_register ( fJackClient, name, JACK_DEFAULT_MIDI_TYPE, port_flags, 0 ) ) == NULL )
  174. return -1;
  175. jack_port_set_latency ( fMidiPlaybackPorts[i], ( fParams.fNetworkMode == 'f' ) ? 0 : port_latency + ( fParams.fSlaveSyncMode ) ? 0 : port_latency );
  176. }
  177. return 0;
  178. }
  179. void JackNetMaster::FreePorts()
  180. {
  181. jack_log ( "JackNetMaster::FreePorts, ID %u", fParams.fID );
  182. uint port_index;
  183. for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ )
  184. if ( fAudioCapturePorts[port_index] )
  185. jack_port_unregister ( fJackClient, fAudioCapturePorts[port_index] );
  186. for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ )
  187. if ( fAudioPlaybackPorts[port_index] )
  188. jack_port_unregister ( fJackClient, fAudioPlaybackPorts[port_index] );
  189. for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ )
  190. if ( fMidiCapturePorts[port_index] )
  191. jack_port_unregister ( fJackClient, fMidiCapturePorts[port_index] );
  192. for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ )
  193. if ( fMidiPlaybackPorts[port_index] )
  194. jack_port_unregister ( fJackClient, fMidiPlaybackPorts[port_index] );
  195. }
  196. int JackNetMaster::SetSyncPacket()
  197. {
  198. if ( fParams.fTransportSync )
  199. {
  200. //TODO : set the TransportData
  201. //copy to TxBuffer
  202. memcpy ( fTxData, &fTransportData, sizeof ( net_transport_data_t ) );
  203. }
  204. return 0;
  205. }
  206. int JackNetMaster::SetProcess ( jack_nframes_t nframes, void* arg )
  207. {
  208. JackNetMaster* master = static_cast<JackNetMaster*> ( arg );
  209. return master->Process();
  210. }
  211. int JackNetMaster::Process()
  212. {
  213. if ( !fRunning )
  214. return 0;
  215. uint port_index;
  216. int res = 0;
  217. #ifdef JACK_MONITOR
  218. jack_time_t begin_time = jack_get_time();
  219. fNetTimeMon->New();
  220. #endif
  221. //buffers
  222. for ( port_index = 0; port_index < fParams.fSendMidiChannels; port_index++ )
  223. fNetMidiCaptureBuffer->SetBuffer ( port_index, static_cast<JackMidiBuffer*> ( jack_port_get_buffer ( fMidiCapturePorts[port_index],
  224. fParams.fPeriodSize ) ) );
  225. for ( port_index = 0; port_index < fParams.fSendAudioChannels; port_index++ )
  226. fNetAudioCaptureBuffer->SetBuffer ( port_index, static_cast<sample_t*> ( jack_port_get_buffer ( fAudioCapturePorts[port_index],
  227. fParams.fPeriodSize ) ) );
  228. for ( port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++ )
  229. fNetMidiPlaybackBuffer->SetBuffer ( port_index, static_cast<JackMidiBuffer*> ( jack_port_get_buffer ( fMidiPlaybackPorts[port_index],
  230. fParams.fPeriodSize ) ) );
  231. for ( port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++ )
  232. fNetAudioPlaybackBuffer->SetBuffer ( port_index, static_cast<sample_t*> ( jack_port_get_buffer ( fAudioPlaybackPorts[port_index],
  233. fParams.fPeriodSize ) ) );
  234. //Set the first packet to send
  235. memset ( fTxData, 0, fPayloadSize );
  236. SetSyncPacket();
  237. //send sync
  238. if ( SyncSend() == SOCKET_ERROR )
  239. return SOCKET_ERROR;
  240. #ifdef JACK_MONITOR
  241. fNetTimeMon->Add ( ( ( ( float ) ( jack_get_time() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f );
  242. #endif
  243. //send data
  244. if ( DataSend() == SOCKET_ERROR )
  245. return SOCKET_ERROR;
  246. #ifdef JACK_MONITOR
  247. fNetTimeMon->Add ( ( ( ( float ) ( jack_get_time() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f );
  248. #endif
  249. //receive sync
  250. res = SyncRecv();
  251. if ( ( res == 0 ) || ( res == SOCKET_ERROR ) )
  252. return res;
  253. #ifdef JACK_MONITOR
  254. fNetTimeMon->Add ( ( ( ( float ) ( jack_get_time() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f );
  255. #endif
  256. //receive data
  257. res = DataRecv();
  258. if ( ( res == 0 ) || ( res == SOCKET_ERROR ) )
  259. return res;
  260. #ifdef JACK_MONITOR
  261. fNetTimeMon->AddLast ( ( ( ( float ) ( jack_get_time() - begin_time ) ) / ( float ) fPeriodUsecs ) * 100.f );
  262. #endif
  263. return 0;
  264. }
  265. //JackNetMasterManager***********************************************************************************************
  266. JackNetMasterManager::JackNetMasterManager ( jack_client_t* client, const JSList* params ) : fSocket()
  267. {
  268. jack_log ( "JackNetMasterManager::JackNetMasterManager" );
  269. fManagerClient = client;
  270. fManagerName = jack_get_client_name ( fManagerClient );
  271. fMulticastIP = DEFAULT_MULTICAST_IP;
  272. fSocket.SetPort ( DEFAULT_PORT );
  273. fGlobalID = 0;
  274. fRunning = true;
  275. const JSList* node;
  276. const jack_driver_param_t* param;
  277. for ( node = params; node; node = jack_slist_next ( node ) )
  278. {
  279. param = ( const jack_driver_param_t* ) node->data;
  280. switch ( param->character )
  281. {
  282. case 'a' :
  283. fMulticastIP = strdup ( param->value.str );
  284. break;
  285. case 'p':
  286. fSocket.SetPort ( param->value.ui );
  287. }
  288. }
  289. //set sync callback
  290. jack_set_sync_callback ( fManagerClient, SetSyncCallback, this );
  291. //activate the client (for sync callback)
  292. if ( jack_activate ( fManagerClient ) != 0 )
  293. jack_error ( "Can't activate the network manager client, transport disabled." );
  294. //launch the manager thread
  295. if ( jack_client_create_thread ( fManagerClient, &fManagerThread, 0, 0, NetManagerThread, this ) )
  296. jack_error ( "Can't create the network manager control thread." );
  297. }
  298. JackNetMasterManager::~JackNetMasterManager()
  299. {
  300. jack_log ( "JackNetMasterManager::~JackNetMasterManager" );
  301. jack_info ( "Exiting net manager..." );
  302. fRunning = false;
  303. jack_client_stop_thread ( fManagerClient, fManagerThread );
  304. master_list_t::iterator it;
  305. for ( it = fMasterList.begin(); it != fMasterList.end(); it++ )
  306. delete ( *it );
  307. fSocket.Close();
  308. SocketAPIEnd();
  309. }
  310. int JackNetMasterManager::SetSyncCallback ( jack_transport_state_t state, jack_position_t* pos, void* arg )
  311. {
  312. JackNetMasterManager* master_manager = static_cast<JackNetMasterManager*> ( arg );
  313. return master_manager->SyncCallback ( state, pos );
  314. }
  315. int JackNetMasterManager::SyncCallback ( jack_transport_state_t state, jack_position_t* pos )
  316. {
  317. //check sync state for every master in the list
  318. int ret = 1;
  319. master_list_it_t it;
  320. for ( it = fMasterList.begin(); it != fMasterList.end(); it++ )
  321. if ( ( *it )->fSyncState == 0 )
  322. ret = 0;
  323. jack_log ( "JackNetMasterManager::SyncCallback returns '%s'", ( ret ) ? "true" : "false" );
  324. return ret;
  325. }
  326. void* JackNetMasterManager::NetManagerThread ( void* arg )
  327. {
  328. JackNetMasterManager* master_manager = static_cast<JackNetMasterManager*> ( arg );
  329. jack_info ( "Starting Jack Network Manager." );
  330. jack_info ( "Listening on '%s:%d'", master_manager->fMulticastIP, master_manager->fSocket.GetPort() );
  331. master_manager->Run();
  332. return NULL;
  333. }
  334. void JackNetMasterManager::Run()
  335. {
  336. jack_log ( "JackNetMasterManager::Run" );
  337. //utility variables
  338. int attempt = 0;
  339. //data
  340. session_params_t params;
  341. int rx_bytes = 0;
  342. JackNetMaster* net_master;
  343. //init socket API (win32)
  344. if ( SocketAPIInit() < 0 )
  345. {
  346. jack_error ( "Can't init Socket API, exiting..." );
  347. return;
  348. }
  349. //socket
  350. if ( fSocket.NewSocket() == SOCKET_ERROR )
  351. {
  352. jack_error ( "Can't create the network management input socket : %s", StrError ( NET_ERROR_CODE ) );
  353. return;
  354. }
  355. //bind the socket to the local port
  356. if ( fSocket.Bind () == SOCKET_ERROR )
  357. {
  358. jack_error ( "Can't bind the network manager socket : %s", StrError ( NET_ERROR_CODE ) );
  359. fSocket.Close();
  360. return;
  361. }
  362. //join multicast group
  363. if ( fSocket.JoinMCastGroup ( fMulticastIP ) == SOCKET_ERROR )
  364. jack_error ( "Can't join multicast group : %s", StrError ( NET_ERROR_CODE ) );
  365. //local loop
  366. if ( fSocket.SetLocalLoop() == SOCKET_ERROR )
  367. jack_error ( "Can't set local loop : %s", StrError ( NET_ERROR_CODE ) );
  368. //set a timeout on the multicast receive (the thread can now be cancelled)
  369. if ( fSocket.SetTimeOut ( 2000000 ) == SOCKET_ERROR )
  370. jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) );
  371. jack_info ( "Waiting for a slave..." );
  372. //main loop, wait for data, deal with it and wait again
  373. do
  374. {
  375. rx_bytes = fSocket.CatchHost ( &params, sizeof ( session_params_t ), 0 );
  376. if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) )
  377. {
  378. jack_error ( "Error in receive : %s", StrError ( NET_ERROR_CODE ) );
  379. if ( ++attempt == 10 )
  380. {
  381. jack_error ( "Can't receive on the socket, exiting net manager." );
  382. return;
  383. }
  384. }
  385. if ( rx_bytes == sizeof ( session_params_t ) )
  386. {
  387. switch ( GetPacketType ( &params ) )
  388. {
  389. case SLAVE_AVAILABLE:
  390. if ( ( net_master = MasterInit ( params ) ) )
  391. SessionParamsDisplay ( &net_master->fParams );
  392. else
  393. jack_error ( "Can't init new net master..." );
  394. jack_info ( "Waiting for a slave..." );
  395. break;
  396. case KILL_MASTER:
  397. if ( KillMaster ( &params ) )
  398. jack_info ( "Waiting for a slave..." );
  399. break;
  400. default:
  401. break;
  402. }
  403. }
  404. }
  405. while ( fRunning );
  406. }
  407. JackNetMaster* JackNetMasterManager::MasterInit ( session_params_t& params )
  408. {
  409. jack_log ( "JackNetMasterManager::MasterInit, Slave : %s", params.fName );
  410. //settings
  411. fSocket.GetName ( params.fMasterNetName );
  412. params.fID = ++fGlobalID;
  413. params.fSampleRate = jack_get_sample_rate ( fManagerClient );
  414. params.fPeriodSize = jack_get_buffer_size ( fManagerClient );
  415. params.fBitdepth = 0;
  416. SetFramesPerPacket ( &params );
  417. SetSlaveName ( params );
  418. //create a new master and add it to the list
  419. JackNetMaster* master = new JackNetMaster ( fSocket, params, fMulticastIP );
  420. if ( master->Init() )
  421. {
  422. fMasterList.push_back ( master );
  423. return master;
  424. }
  425. delete master;
  426. return NULL;
  427. }
  428. void JackNetMasterManager::SetSlaveName ( session_params_t& params )
  429. {
  430. jack_log ( "JackNetMasterManager::SetSlaveName" );
  431. master_list_it_t it;
  432. for ( it = fMasterList.begin(); it != fMasterList.end(); it++ )
  433. if ( strcmp ( ( *it )->fParams.fName, params.fName ) == 0 )
  434. sprintf ( params.fName, "%s-%u", params.fName, params.fID );
  435. }
  436. master_list_it_t JackNetMasterManager::FindMaster ( uint32_t id )
  437. {
  438. jack_log ( "JackNetMasterManager::FindMaster, ID %u.", id );
  439. master_list_it_t it;
  440. for ( it = fMasterList.begin(); it != fMasterList.end(); it++ )
  441. if ( ( *it )->fParams.fID == id )
  442. return it;
  443. return it;
  444. }
  445. int JackNetMasterManager::KillMaster ( session_params_t* params )
  446. {
  447. jack_log ( "JackNetMasterManager::KillMaster, ID %u.", params->fID );
  448. master_list_it_t master = FindMaster ( params->fID );
  449. if ( master != fMasterList.end() )
  450. {
  451. fMasterList.erase ( master );
  452. delete *master;
  453. return 1;
  454. }
  455. return 0;
  456. }
  457. }//namespace
  458. static Jack::JackNetMasterManager* master_manager = NULL;
  459. #ifdef __cplusplus
  460. extern "C"
  461. {
  462. #endif
  463. EXPORT jack_driver_desc_t* jack_get_descriptor()
  464. {
  465. jack_driver_desc_t *desc;
  466. desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) );
  467. strcpy ( desc->name, "netmanager" );
  468. desc->nparams = 2;
  469. desc->params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) );
  470. int i = 0;
  471. strcpy ( desc->params[i].name, "multicast_ip" );
  472. desc->params[i].character = 'a';
  473. desc->params[i].type = JackDriverParamString;
  474. strcpy ( desc->params[i].value.str, DEFAULT_MULTICAST_IP );
  475. strcpy ( desc->params[i].short_desc, "Multicast Address" );
  476. strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
  477. i++;
  478. strcpy ( desc->params[i].name, "udp_net_port" );
  479. desc->params[i].character = 'p';
  480. desc->params[i].type = JackDriverParamInt;
  481. desc->params[i].value.i = DEFAULT_PORT;
  482. strcpy ( desc->params[i].short_desc, "UDP port" );
  483. strcpy ( desc->params[i].long_desc, desc->params[i].short_desc );
  484. return desc;
  485. }
  486. EXPORT int jack_internal_initialize ( jack_client_t* jack_client, const JSList* params )
  487. {
  488. if ( master_manager )
  489. {
  490. jack_error ( "Master Manager already loaded" );
  491. return 1;
  492. }
  493. else
  494. {
  495. jack_log ( "Loading Master Manager" );
  496. master_manager = new Jack::JackNetMasterManager ( jack_client, params );
  497. return ( master_manager ) ? 0 : 1;
  498. }
  499. }
  500. EXPORT int jack_initialize ( jack_client_t* jack_client, const char* load_init )
  501. {
  502. JSList* params = NULL;
  503. jack_driver_desc_t* desc = jack_get_descriptor();
  504. Jack::JackArgParser parser ( load_init );
  505. if ( parser.GetArgc() > 0 )
  506. {
  507. if ( parser.ParseParams ( desc, &params ) < 0 )
  508. jack_error ( "Internal client JackArgParser::ParseParams error." );
  509. }
  510. return jack_internal_initialize ( jack_client, params );
  511. }
  512. EXPORT void jack_finish ( void* arg )
  513. {
  514. if ( master_manager )
  515. {
  516. jack_log ( "Unloading Master Manager" );
  517. delete master_manager;
  518. master_manager = NULL;
  519. }
  520. }
  521. #ifdef __cplusplus
  522. }
  523. #endif