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