Jack2  1.9.8
JackEngine.cpp
1 /*
2 Copyright (C) 2004-2008 Grame
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <iostream>
21 #include <fstream>
22 #include <set>
23 #include <assert.h>
24 
25 #include "JackSystemDeps.h"
26 #include "JackLockedEngine.h"
27 #include "JackExternalClient.h"
28 #include "JackInternalClient.h"
29 #include "JackEngineControl.h"
30 #include "JackClientControl.h"
31 #include "JackServerGlobals.h"
32 #include "JackGlobals.h"
33 #include "JackChannel.h"
34 #include "JackError.h"
35 
36 namespace Jack
37 {
38 
39 JackEngine::JackEngine(JackGraphManager* manager,
40  JackSynchro* table,
41  JackEngineControl* control)
42 {
43  fGraphManager = manager;
44  fSynchroTable = table;
45  fEngineControl = control;
46  for (int i = 0; i < CLIENT_NUM; i++)
47  fClientTable[i] = NULL;
48  fLastSwitchUsecs = 0;
49  fMaxUUID = 0;
50  fSessionPendingReplies = 0;
51  fSessionTransaction = NULL;
52  fSessionResult = NULL;
53 }
54 
55 JackEngine::~JackEngine()
56 {}
57 
58 int JackEngine::Open()
59 {
60  jack_log("JackEngine::Open");
61 
62  // Open audio thread => request thread communication channel
63  if (fChannel.Open(fEngineControl->fServerName) < 0) {
64  jack_error("Cannot connect to server");
65  return -1;
66  } else {
67  return 0;
68  }
69 }
70 
71 int JackEngine::Close()
72 {
73  jack_log("JackEngine::Close");
74  fChannel.Close();
75 
76  // Close remaining clients (RT is stopped)
77  for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
78  if (JackLoadableInternalClient* loadable_client = dynamic_cast<JackLoadableInternalClient*>(fClientTable[i])) {
79  jack_log("JackEngine::Close loadable client = %s", loadable_client->GetClientControl()->fName);
80  loadable_client->Close();
81  // Close does not delete the pointer for internal clients
82  fClientTable[i] = NULL;
83  delete loadable_client;
84  } else if (JackExternalClient* external_client = dynamic_cast<JackExternalClient*>(fClientTable[i])) {
85  jack_log("JackEngine::Close external client = %s", external_client->GetClientControl()->fName);
86  external_client->Close();
87  // Close deletes the pointer for external clients
88  fClientTable[i] = NULL;
89  }
90  }
91 
92  return 0;
93 }
94 
95 void JackEngine::NotifyQuit()
96 {
97  fChannel.NotifyQuit();
98 }
99 
100 //-----------------------------
101 // Client ressource management
102 //-----------------------------
103 
104 int JackEngine::AllocateRefnum()
105 {
106  for (int i = 0; i < CLIENT_NUM; i++) {
107  if (!fClientTable[i]) {
108  jack_log("JackEngine::AllocateRefNum ref = %ld", i);
109  return i;
110  }
111  }
112  return -1;
113 }
114 
115 void JackEngine::ReleaseRefnum(int ref)
116 {
117  fClientTable[ref] = NULL;
118 
119  if (fEngineControl->fTemporary) {
120  int i;
121  for (i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
122  if (fClientTable[i])
123  break;
124  }
125  if (i == CLIENT_NUM) {
126  // last client and temporay case: quit the server
127  jack_log("JackEngine::ReleaseRefnum server quit");
128  fEngineControl->fTemporary = false;
129  throw JackTemporaryException();
130  }
131  }
132 }
133 
134 //------------------
135 // Graph management
136 //------------------
137 
138 void JackEngine::ProcessNext(jack_time_t cur_cycle_begin)
139 {
140  fLastSwitchUsecs = cur_cycle_begin;
141  if (fGraphManager->RunNextGraph()) { // True if the graph actually switched to a new state
142  fChannel.Notify(ALL_CLIENTS, kGraphOrderCallback, 0);
143  }
144  fSignal.Signal(); // Signal for threads waiting for next cycle
145 }
146 
147 void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin)
148 {
149  if (cur_cycle_begin < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failing cycle
150  CheckXRun(cur_cycle_begin);
151  fGraphManager->RunCurrentGraph();
152 }
153 
154 bool JackEngine::Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end)
155 {
156  bool res = true;
157 
158  // Cycle begin
159  fEngineControl->CycleBegin(fClientTable, fGraphManager, cur_cycle_begin, prev_cycle_end);
160 
161  // Graph
162  if (fGraphManager->IsFinishedGraph()) {
163  ProcessNext(cur_cycle_begin);
164  res = true;
165  } else {
166  jack_log("Process: graph not finished!");
167  if (cur_cycle_begin > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
168  jack_log("Process: switch to next state delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
169  ProcessNext(cur_cycle_begin);
170  res = true;
171  } else {
172  jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
173  ProcessCurrent(cur_cycle_begin);
174  res = false;
175  }
176  }
177 
178  // Cycle end
179  fEngineControl->CycleEnd(fClientTable);
180  return res;
181 }
182 
183 /*
184 Client that finish *after* the callback date are considered late even if their output buffers may have been
185 correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
186 */
187 
188 void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions de fin
189 {
190  for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
191  JackClientInterface* client = fClientTable[i];
192  if (client && client->GetClientControl()->fActive) {
193  JackClientTiming* timing = fGraphManager->GetClientTiming(i);
194  jack_client_state_t status = timing->fStatus;
195  jack_time_t finished_date = timing->fFinishedAt;
196 
197  if (status != NotTriggered && status != Finished) {
198  jack_error("JackEngine::XRun: client = %s was not run: state = %ld", client->GetClientControl()->fName, status);
199  fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients
200  }
201 
202  if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
203  jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
204  fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients
205  }
206  }
207  }
208 }
209 
210 int JackEngine::ComputeTotalLatencies()
211 {
212  std::vector<jack_int_t> sorted;
213  std::vector<jack_int_t>::iterator it;
214  std::vector<jack_int_t>::reverse_iterator rit;
215 
216  fGraphManager->TopologicalSort(sorted);
217 
218  /* iterate over all clients in graph order, and emit
219  * capture latency callback.
220  */
221 
222  for (it = sorted.begin(); it != sorted.end(); it++) {
223  NotifyClient(*it, kLatencyCallback, true, "", 0, 0);
224  }
225 
226  /* now issue playback latency callbacks in reverse graph order.
227  */
228  for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) {
229  NotifyClient(*rit, kLatencyCallback, true, "", 1, 0);
230  }
231 
232  return 0;
233 }
234 
235 //---------------
236 // Notifications
237 //---------------
238 
239 void JackEngine::NotifyClient(int refnum, int event, int sync, const char* message, int value1, int value2)
240 {
241  JackClientInterface* client = fClientTable[refnum];
242 
243  // The client may be notified by the RT thread while closing
244  if (client) {
245 
246  if (client->GetClientControl()->fCallback[event]) {
247  /*
248  Important for internal clients : unlock before calling the notification callbacks.
249  */
250  bool res = Unlock();
251  if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, message, value1, value2) < 0)
252  jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2);
253  if (res)
254  Lock();
255 
256  } else {
257  jack_log("JackEngine::NotifyClient: no callback for event = %ld", event);
258  }
259  }
260 }
261 
262 void JackEngine::NotifyClients(int event, int sync, const char* message, int value1, int value2)
263 {
264  for (int i = 0; i < CLIENT_NUM; i++) {
265  NotifyClient(i, event, sync, message, value1, value2);
266  }
267 }
268 
269 int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
270 {
271  jack_log("JackEngine::NotifyAddClient: name = %s", name);
272  // Notify existing clients of the new client and new client of existing clients.
273  for (int i = 0; i < CLIENT_NUM; i++) {
274  JackClientInterface* old_client = fClientTable[i];
275  if (old_client && old_client != new_client) {
276  if (old_client->ClientNotify(refnum, name, kAddClient, false, "", 0, 0) < 0) {
277  jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName);
278  // Not considered as a failure...
279  }
280  if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, kAddClient, true, "", 0, 0) < 0) {
281  jack_error("NotifyAddClient new_client fails name = %s", name);
282  return -1;
283  }
284  }
285  }
286 
287  return 0;
288 }
289 
290 void JackEngine::NotifyRemoveClient(const char* name, int refnum)
291 {
292  // Notify existing clients (including the one beeing suppressed) of the removed client
293  for (int i = 0; i < CLIENT_NUM; i++) {
294  JackClientInterface* client = fClientTable[i];
295  if (client) {
296  client->ClientNotify(refnum, name, kRemoveClient, false, "", 0, 0);
297  }
298  }
299 }
300 
301 // Coming from the driver
302 void JackEngine::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs)
303 {
304  // Use the audio thread => request thread communication channel
305  fEngineControl->NotifyXRun(callback_usecs, delayed_usecs);
306  fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);
307 }
308 
309 void JackEngine::NotifyXRun(int refnum)
310 {
311  if (refnum == ALL_CLIENTS) {
312  NotifyClients(kXRunCallback, false, "", 0, 0);
313  } else {
314  NotifyClient(refnum, kXRunCallback, false, "", 0, 0);
315  }
316 }
317 
318 void JackEngine::NotifyGraphReorder()
319 {
320  NotifyClients(kGraphOrderCallback, false, "", 0, 0);
321  ComputeTotalLatencies();
322 }
323 
324 void JackEngine::NotifyBufferSize(jack_nframes_t buffer_size)
325 {
326  NotifyClients(kBufferSizeCallback, true, "", buffer_size, 0);
327 }
328 
329 void JackEngine::NotifySampleRate(jack_nframes_t sample_rate)
330 {
331  NotifyClients(kSampleRateCallback, true, "", sample_rate, 0);
332 }
333 
334 void JackEngine::NotifyFailure(int code, const char* reason)
335 {
336  NotifyClients(kShutDownCallback, false, reason, code, 0);
337 }
338 
339 void JackEngine::NotifyFreewheel(bool onoff)
340 {
341  if (onoff) {
342  // Save RT state
343  fEngineControl->fSavedRealTime = fEngineControl->fRealTime;
344  fEngineControl->fRealTime = false;
345  } else {
346  // Restore RT state
347  fEngineControl->fRealTime = fEngineControl->fSavedRealTime;
348  fEngineControl->fSavedRealTime = false;
349  }
350  NotifyClients((onoff ? kStartFreewheelCallback : kStopFreewheelCallback), true, "", 0, 0);
351 }
352 
353 void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
354 {
355  NotifyClients((onoff ? kPortRegistrationOnCallback : kPortRegistrationOffCallback), false, "", port_index, 0);
356 }
357 
358 void JackEngine::NotifyPortRename(jack_port_id_t port, const char* old_name)
359 {
360  NotifyClients(kPortRenameCallback, false, old_name, port, 0);
361 }
362 
363 void JackEngine::NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff)
364 {
365  NotifyClients((onoff ? kPortConnectCallback : kPortDisconnectCallback), false, "", src, dst);
366 }
367 
368 void JackEngine::NotifyActivate(int refnum)
369 {
370  NotifyClient(refnum, kActivateClient, true, "", 0, 0);
371 }
372 
373 //----------------------------
374 // Loadable client management
375 //----------------------------
376 
377 int JackEngine::GetInternalClientName(int refnum, char* name_res)
378 {
379  JackClientInterface* client = fClientTable[refnum];
380  strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
381  return 0;
382 }
383 
384 int JackEngine::InternalClientHandle(const char* client_name, int* status, int* int_ref)
385 {
386  // Clear status
387  *status = 0;
388 
389  for (int i = 0; i < CLIENT_NUM; i++) {
390  JackClientInterface* client = fClientTable[i];
391  if (client && dynamic_cast<JackLoadableInternalClient*>(client) && (strcmp(client->GetClientControl()->fName, client_name) == 0)) {
392  jack_log("InternalClientHandle found client name = %s ref = %ld", client_name, i);
393  *int_ref = i;
394  return 0;
395  }
396  }
397 
398  *status |= (JackNoSuchClient | JackFailure);
399  return -1;
400 }
401 
402 int JackEngine::InternalClientUnload(int refnum, int* status)
403 {
404  JackClientInterface* client = fClientTable[refnum];
405  if (client) {
406  int res = client->Close();
407  delete client;
408  *status = 0;
409  return res;
410  } else {
411  *status = (JackNoSuchClient | JackFailure);
412  return -1;
413  }
414 }
415 
416 //-------------------
417 // Client management
418 //-------------------
419 
420 int JackEngine::ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status)
421 {
422  // Clear status
423  *status = 0;
424  strcpy(name_res, name);
425 
426  jack_log("Check protocol client = %ld server = %ld", protocol, JACK_PROTOCOL_VERSION);
427 
428  if (protocol != JACK_PROTOCOL_VERSION) {
429  *status |= (JackFailure | JackVersionError);
430  jack_error("JACK protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION);
431  return -1;
432  }
433 
434  std::map<int,std::string>::iterator res = fReservationMap.find(uuid);
435 
436  if (res != fReservationMap.end()) {
437  strncpy(name_res, res->second.c_str(), JACK_CLIENT_NAME_SIZE);
438  } else if (ClientCheckName(name)) {
439 
440  *status |= JackNameNotUnique;
441 
442  if (options & JackUseExactName) {
443  jack_error("cannot create new client; %s already exists", name);
444  *status |= JackFailure;
445  return -1;
446  }
447 
448  if (GenerateUniqueName(name_res)) {
449  *status |= JackFailure;
450  return -1;
451  }
452  }
453 
454  return 0;
455 }
456 
457 bool JackEngine::GenerateUniqueName(char* name)
458 {
459  int tens, ones;
460  int length = strlen(name);
461 
462  if (length > JACK_CLIENT_NAME_SIZE - 4) {
463  jack_error("%s exists and is too long to make unique", name);
464  return true; /* failure */
465  }
466 
467  /* generate a unique name by appending "-01".."-99" */
468  name[length++] = '-';
469  tens = length++;
470  ones = length++;
471  name[tens] = '0';
472  name[ones] = '1';
473  name[length] = '\0';
474 
475  while (ClientCheckName(name)) {
476  if (name[ones] == '9') {
477  if (name[tens] == '9') {
478  jack_error("client %s has 99 extra instances already", name);
479  return true; /* give up */
480  }
481  name[tens]++;
482  name[ones] = '0';
483  } else {
484  name[ones]++;
485  }
486  }
487  return false;
488 }
489 
490 bool JackEngine::ClientCheckName(const char* name)
491 {
492  for (int i = 0; i < CLIENT_NUM; i++) {
493  JackClientInterface* client = fClientTable[i];
494  if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
495  return true;
496  }
497 
498  for (std::map<int,std::string>::iterator i = fReservationMap.begin(); i != fReservationMap.end(); i++) {
499  if (i->second == name)
500  return true;
501  }
502 
503  return false;
504 }
505 
506 int JackEngine::GetNewUUID()
507 {
508  return fMaxUUID++;
509 }
510 
511 void JackEngine::EnsureUUID(int uuid)
512 {
513  if (uuid > fMaxUUID)
514  fMaxUUID = uuid+1;
515 
516  for (int i = 0; i < CLIENT_NUM; i++) {
517  JackClientInterface* client = fClientTable[i];
518  if (client && (client->GetClientControl()->fSessionID == uuid)) {
519  client->GetClientControl()->fSessionID = GetNewUUID();
520  }
521  }
522 }
523 
524 int JackEngine::GetClientPID(const char* name)
525 {
526  for (int i = 0; i < CLIENT_NUM; i++) {
527  JackClientInterface* client = fClientTable[i];
528  if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
529  return client->GetClientControl()->fPID;
530  }
531 
532  return 0;
533 }
534 
535 int JackEngine::GetClientRefNum(const char* name)
536 {
537  for (int i = 0; i < CLIENT_NUM; i++) {
538  JackClientInterface* client = fClientTable[i];
539  if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
540  return client->GetClientControl()->fRefNum;
541  }
542 
543  return -1;
544 }
545 
546 // Used for external clients
547 int JackEngine::ClientExternalOpen(const char* name, int pid, int uuid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
548 {
549  char real_name[JACK_CLIENT_NAME_SIZE + 1];
550 
551  if (uuid < 0) {
552  uuid = GetNewUUID();
553  strncpy(real_name, name, JACK_CLIENT_NAME_SIZE);
554  } else {
555  std::map<int, std::string>::iterator res = fReservationMap.find(uuid);
556  if (res != fReservationMap.end()) {
557  strncpy(real_name, res->second.c_str(), JACK_CLIENT_NAME_SIZE);
558  fReservationMap.erase(uuid);
559  } else {
560  strncpy(real_name, name, JACK_CLIENT_NAME_SIZE);
561  }
562  EnsureUUID(uuid);
563  }
564 
565  jack_log("JackEngine::ClientExternalOpen: uuid = %d, name = %s ", uuid, real_name);
566 
567  int refnum = AllocateRefnum();
568  if (refnum < 0) {
569  jack_error("No more refnum available");
570  return -1;
571  }
572 
573  JackExternalClient* client = new JackExternalClient();
574 
575  if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) {
576  jack_error("Cannot allocate synchro");
577  goto error;
578  }
579 
580  if (client->Open(real_name, pid, refnum, uuid, shared_client) < 0) {
581  jack_error("Cannot open client");
582  goto error;
583  }
584 
585  if (!fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
586  // Failure if RT thread is not running (problem with the driver...)
587  jack_error("Driver is not running");
588  goto error;
589  }
590 
591  fClientTable[refnum] = client;
592 
593  if (NotifyAddClient(client, real_name, refnum) < 0) {
594  jack_error("Cannot notify add client");
595  goto error;
596  }
597 
598  fGraphManager->InitRefNum(refnum);
599  fEngineControl->ResetRollingUsecs();
600  *shared_engine = fEngineControl->GetShmIndex();
601  *shared_graph_manager = fGraphManager->GetShmIndex();
602  *ref = refnum;
603  return 0;
604 
605 error:
606  // Cleanup...
607  fSynchroTable[refnum].Destroy();
608  fClientTable[refnum] = 0;
609  client->Close();
610  delete client;
611  return -1;
612 }
613 
614 // Used for server driver clients
615 int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait)
616 {
617  jack_log("JackEngine::ClientInternalOpen: name = %s", name);
618 
619  int refnum = AllocateRefnum();
620  if (refnum < 0) {
621  jack_error("No more refnum available");
622  goto error;
623  }
624 
625  if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
626  jack_error("Cannot allocate synchro");
627  goto error;
628  }
629 
630  if (wait && !fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
631  // Failure if RT thread is not running (problem with the driver...)
632  jack_error("Driver is not running");
633  goto error;
634  }
635 
636  fClientTable[refnum] = client;
637 
638  if (NotifyAddClient(client, name, refnum) < 0) {
639  jack_error("Cannot notify add client");
640  goto error;
641  }
642 
643  fGraphManager->InitRefNum(refnum);
644  fEngineControl->ResetRollingUsecs();
645  *shared_engine = fEngineControl;
646  *shared_manager = fGraphManager;
647  *ref = refnum;
648  return 0;
649 
650 error:
651  // Cleanup...
652  fSynchroTable[refnum].Destroy();
653  fClientTable[refnum] = 0;
654  return -1;
655 }
656 
657 // Used for external clients
658 int JackEngine::ClientExternalClose(int refnum)
659 {
660  JackClientInterface* client = fClientTable[refnum];
661  fEngineControl->fTransport.ResetTimebase(refnum);
662  int res = ClientCloseAux(refnum, client, true);
663  client->Close();
664  delete client;
665  return res;
666 }
667 
668 // Used for server internal clients or drivers when the RT thread is stopped
669 int JackEngine::ClientInternalClose(int refnum, bool wait)
670 {
671  JackClientInterface* client = fClientTable[refnum];
672  return ClientCloseAux(refnum, client, wait);
673 }
674 
675 int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
676 {
677  jack_log("JackEngine::ClientCloseAux ref = %ld", refnum);
678 
679  // Unregister all ports ==> notifications are sent
680  jack_int_t ports[PORT_NUM_FOR_CLIENT];
681  int i;
682 
683  fGraphManager->GetInputPorts(refnum, ports);
684  for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY); i++) {
685  PortUnRegister(refnum, ports[i]);
686  }
687 
688  fGraphManager->GetOutputPorts(refnum, ports);
689  for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY); i++) {
690  PortUnRegister(refnum, ports[i]);
691  }
692 
693  // Remove the client from the table
694  ReleaseRefnum(refnum);
695 
696  // Remove all ports
697  fGraphManager->RemoveAllPorts(refnum);
698 
699  // Wait until next cycle to be sure client is not used anymore
700  if (wait) {
701  if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
702  jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum);
703  }
704  }
705 
706  // Notify running clients
707  NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
708 
709  // Cleanup...
710  fSynchroTable[refnum].Destroy();
711  fEngineControl->ResetRollingUsecs();
712  return 0;
713 }
714 
715 int JackEngine::ClientActivate(int refnum, bool is_real_time)
716 {
717  JackClientInterface* client = fClientTable[refnum];
718  jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
719 
720  if (is_real_time)
721  fGraphManager->Activate(refnum);
722 
723  // Wait for graph state change to be effective
724  if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
725  jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
726  return -1;
727  } else {
728  jack_int_t input_ports[PORT_NUM_FOR_CLIENT];
729  jack_int_t output_ports[PORT_NUM_FOR_CLIENT];
730  fGraphManager->GetInputPorts(refnum, input_ports);
731  fGraphManager->GetOutputPorts(refnum, output_ports);
732 
733  // Notify client
734  NotifyActivate(refnum);
735 
736  // Then issue port registration notification
737  for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
738  NotifyPortRegistation(input_ports[i], true);
739  }
740  for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
741  NotifyPortRegistation(output_ports[i], true);
742  }
743 
744  return 0;
745  }
746 }
747 
748 // May be called without client
749 int JackEngine::ClientDeactivate(int refnum)
750 {
751  JackClientInterface* client = fClientTable[refnum];
752  jack_log("JackEngine::ClientDeactivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
753 
754  jack_int_t input_ports[PORT_NUM_FOR_CLIENT];
755  jack_int_t output_ports[PORT_NUM_FOR_CLIENT];
756  fGraphManager->GetInputPorts(refnum, input_ports);
757  fGraphManager->GetOutputPorts(refnum, output_ports);
758 
759  // First disconnect all ports
760  for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
761  PortDisconnect(refnum, input_ports[i], ALL_PORTS);
762  }
763  for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
764  PortDisconnect(refnum, output_ports[i], ALL_PORTS);
765  }
766 
767  // Then issue port registration notification
768  for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
769  NotifyPortRegistation(input_ports[i], false);
770  }
771  for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
772  NotifyPortRegistation(output_ports[i], false);
773  }
774 
775  fGraphManager->Deactivate(refnum);
776  fLastSwitchUsecs = 0; // Force switch to occur next cycle, even when called with "dead" clients
777 
778  // Wait for graph state change to be effective
779  if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
780  jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
781  return -1;
782  } else {
783  return 0;
784  }
785 }
786 
787 //-----------------
788 // Port management
789 //-----------------
790 
791 int JackEngine::PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
792 {
793  jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum, name, type, flags, buffer_size);
794  JackClientInterface* client = fClientTable[refnum];
795 
796  // Check if port name already exists
797  if (fGraphManager->GetPort(name) != NO_PORT) {
798  jack_error("port_name \"%s\" already exists", name);
799  return -1;
800  }
801 
802  // buffer_size is actually ignored...
803  *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize);
804  if (*port_index != NO_PORT) {
805  if (client->GetClientControl()->fActive)
806  NotifyPortRegistation(*port_index, true);
807  return 0;
808  } else {
809  return -1;
810  }
811 }
812 
813 int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
814 {
815  jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum, port_index);
816  JackClientInterface* client = fClientTable[refnum];
817 
818  // Disconnect port ==> notification is sent
819  PortDisconnect(refnum, port_index, ALL_PORTS);
820 
821  if (fGraphManager->ReleasePort(refnum, port_index) == 0) {
822  if (client->GetClientControl()->fActive)
823  NotifyPortRegistation(port_index, false);
824  return 0;
825  } else {
826  return -1;
827  }
828 }
829 
830 int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
831 {
832  jack_log("JackEngine::PortConnect src = %s dst = %s", src, dst);
833  jack_port_id_t port_src, port_dst;
834 
835  return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
836  ? -1
837  : PortConnect(refnum, port_src, port_dst);
838 }
839 
840 int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
841 {
842  jack_log("JackEngine::PortConnect src = %d dst = %d", src, dst);
843  JackClientInterface* client;
844  int ref;
845 
846  if (fGraphManager->CheckPorts(src, dst) < 0)
847  return -1;
848 
849  ref = fGraphManager->GetOutputRefNum(src);
850  assert(ref >= 0);
851  client = fClientTable[ref];
852  assert(client);
853  if (!client->GetClientControl()->fActive) {
854  jack_error("Cannot connect ports owned by inactive clients:"
855  " \"%s\" is not active", client->GetClientControl()->fName);
856  return -1;
857  }
858 
859  ref = fGraphManager->GetInputRefNum(dst);
860  assert(ref >= 0);
861  client = fClientTable[ref];
862  assert(client);
863  if (!client->GetClientControl()->fActive) {
864  jack_error("Cannot connect ports owned by inactive clients:"
865  " \"%s\" is not active", client->GetClientControl()->fName);
866  return -1;
867  }
868 
869  int res = fGraphManager->Connect(src, dst);
870  if (res == 0)
871  NotifyPortConnect(src, dst, true);
872  return res;
873 }
874 
875 int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
876 {
877  jack_log("JackEngine::PortDisconnect src = %s dst = %s", src, dst);
878  jack_port_id_t port_src, port_dst;
879 
880  return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
881  ? -1
882  : PortDisconnect(refnum, port_src, port_dst);
883 }
884 
885 int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
886 {
887  jack_log("JackEngine::PortDisconnect src = %d dst = %d", src, dst);
888 
889  if (dst == ALL_PORTS) {
890 
891  jack_int_t connections[CONNECTION_NUM_FOR_PORT];
892  fGraphManager->GetConnections(src, connections);
893 
894  JackPort* port = fGraphManager->GetPort(src);
895  int ret = 0;
896  if (port->GetFlags() & JackPortIsOutput) {
897  for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
898  if (PortDisconnect(refnum, src, connections[i]) != 0) {
899  ret = -1;
900  }
901  }
902  } else {
903  for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
904  if (PortDisconnect(refnum, connections[i], src) != 0) {
905  ret = -1;
906  }
907  }
908  }
909 
910  return ret;
911  } else if (fGraphManager->CheckPorts(src, dst) < 0) {
912  return -1;
913  } else if (fGraphManager->Disconnect(src, dst) == 0) {
914  // Notifications
915  NotifyPortConnect(src, dst, false);
916  return 0;
917  } else {
918  return -1;
919  }
920 }
921 
922 int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name)
923 {
924  char old_name[REAL_JACK_PORT_NAME_SIZE];
925  strcpy(old_name, fGraphManager->GetPort(port)->GetName());
926  fGraphManager->GetPort(port)->SetName(name);
927  NotifyPortRename(port, old_name);
928  return 0;
929 }
930 
931 //--------------------
932 // Session management
933 //--------------------
934 
935 void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result)
936 {
937  if (fSessionPendingReplies != 0) {
938  JackSessionNotifyResult res(-1);
939  res.Write(socket);
940  jack_log("JackEngine::SessionNotify ... busy");
941  if (result != NULL) {
942  *result = NULL;
943  }
944  return;
945  }
946 
947  for (int i = 0; i < CLIENT_NUM; i++) {
948  JackClientInterface* client = fClientTable[i];
949  if (client && (client->GetClientControl()->fSessionID < 0)) {
950  client->GetClientControl()->fSessionID = GetNewUUID();
951  }
952  }
953  fSessionResult = new JackSessionNotifyResult();
954 
955  for (int i = 0; i < CLIENT_NUM; i++) {
956  JackClientInterface* client = fClientTable[i];
957  if (client && client->GetClientControl()->fCallback[kSessionCallback]) {
958 
959  // check if this is a notification to a specific client.
960  if (target != NULL && strlen(target) != 0) {
961  if (strcmp(target, client->GetClientControl()->fName)) {
962  continue;
963  }
964  }
965 
966  char path_buf[JACK_PORT_NAME_SIZE];
967  snprintf(path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR);
968 
969  int res = JackTools::MkDir(path_buf);
970  if (res)
971  jack_error("JackEngine::SessionNotify: can not create session directory '%s'", path_buf);
972 
973  int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int)type, 0);
974 
975  if (result == kPendingSessionReply) {
976  fSessionPendingReplies += 1;
977  } else if (result == kImmediateSessionReply) {
978  char uuid_buf[JACK_UUID_SIZE];
979  snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID);
980  fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf,
981  client->GetClientControl()->fName,
982  client->GetClientControl()->fSessionCommand,
983  client->GetClientControl()->fSessionFlags));
984  }
985  }
986  }
987 
988  if (result != NULL) {
989  *result = fSessionResult;
990  }
991 
992  if (fSessionPendingReplies == 0) {
993  fSessionResult->Write(socket);
994  if (result == NULL) {
995  delete fSessionResult;
996  }
997  fSessionResult = NULL;
998  } else {
999  fSessionTransaction = socket;
1000  }
1001 }
1002 
1003 void JackEngine::SessionReply(int refnum)
1004 {
1005  JackClientInterface* client = fClientTable[refnum];
1006  char uuid_buf[JACK_UUID_SIZE];
1007  snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID);
1008  fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf,
1009  client->GetClientControl()->fName,
1010  client->GetClientControl()->fSessionCommand,
1011  client->GetClientControl()->fSessionFlags));
1012  fSessionPendingReplies -= 1;
1013 
1014  if (fSessionPendingReplies == 0) {
1015  fSessionResult->Write(fSessionTransaction);
1016  if (fSessionTransaction != NULL)
1017  {
1018  delete fSessionResult;
1019  }
1020  fSessionResult = NULL;
1021  }
1022 }
1023 
1024 void JackEngine::GetUUIDForClientName(const char *client_name, char *uuid_res, int *result)
1025 {
1026  for (int i = 0; i < CLIENT_NUM; i++) {
1027  JackClientInterface* client = fClientTable[i];
1028 
1029  if (client && (strcmp(client_name, client->GetClientControl()->fName) == 0)) {
1030  snprintf(uuid_res, JACK_UUID_SIZE, "%d", client->GetClientControl()->fSessionID);
1031  *result = 0;
1032  return;
1033  }
1034  }
1035  // Did not find name.
1036  *result = -1;
1037 }
1038 
1039 void JackEngine::GetClientNameForUUID(const char *uuid, char *name_res, int *result)
1040 {
1041  for (int i = 0; i < CLIENT_NUM; i++) {
1042  JackClientInterface* client = fClientTable[i];
1043 
1044  if (!client)
1045  continue;
1046 
1047  char uuid_buf[JACK_UUID_SIZE];
1048  snprintf(uuid_buf, JACK_UUID_SIZE, "%d", client->GetClientControl()->fSessionID);
1049 
1050  if (strcmp(uuid,uuid_buf) == 0) {
1051  strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
1052  *result = 0;
1053  return;
1054  }
1055  }
1056  // Did not find uuid.
1057  *result = -1;
1058 }
1059 
1060 void JackEngine::ReserveClientName(const char *name, const char *uuid, int *result)
1061 {
1062  jack_log("JackEngine::ReserveClientName ( name = %s, uuid = %s )", name, uuid);
1063 
1064  if (ClientCheckName(name)) {
1065  *result = -1;
1066  jack_log("name already taken");
1067  return;
1068  }
1069 
1070  EnsureUUID(atoi(uuid));
1071  fReservationMap[atoi(uuid)] = name;
1072  *result = 0;
1073 }
1074 
1075 void JackEngine::ClientHasSessionCallback(const char *name, int *result)
1076 {
1077  JackClientInterface* client = NULL;
1078  for (int i = 0; i < CLIENT_NUM; i++) {
1079  client = fClientTable[i];
1080  if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
1081  break;
1082  }
1083 
1084  if (client) {
1085  *result = client->GetClientControl()->fCallback[kSessionCallback];
1086  } else {
1087  *result = -1;
1088  }
1089 }
1090 
1091 } // end of namespace
1092