Move client refnum management in JackEngine.
[jack2.git] / common / JackEngine.cpp
blob858a8faa4c378b075dda1bff8cce5a54baae2fc4
1 /*
2 Copyright (C) 2004-2006 Grame
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.
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.
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.
20 #include <iostream>
21 #include <fstream>
22 #include <assert.h>
24 #include "JackEngine.h"
25 #include "JackExternalClient.h"
26 #include "JackEngineControl.h"
27 #include "JackClientControl.h"
28 #include "JackEngineTiming.h"
29 #include "JackGlobals.h"
30 #include "JackChannel.h"
31 #include "JackSyncInterface.h"
33 namespace Jack
36 JackEngine::JackEngine(JackGraphManager* manager, JackSynchro** table, JackEngineControl* control, JackSyncInterface* signal, bool sync, long time_out_ms, bool rt, long priority, bool ve)
38 fGraphManager = manager;
39 fSynchroTable = table;
40 fEngineControl = control;
41 fEngineControl->fSyncMode = sync;
42 fEngineControl->fTimeOutUsecs = time_out_ms * 1000;
43 fEngineControl->fRealTime = rt;
44 fEngineControl->fPriority = priority;
45 fEngineControl->fVerbose = ve;
46 fChannel = JackGlobals::MakeServerNotifyChannel();
47 fEngineTiming = new JackEngineTiming(fClientTable, fGraphManager, fEngineControl);
48 fSignal = signal;
49 for (int i = 0; i < CLIENT_NUM; i++)
50 fClientTable[i] = NULL;
51 fEngineTiming->ClearTimeMeasures();
52 fEngineTiming->ResetRollingUsecs();
55 JackEngine::~JackEngine()
57 delete fChannel;
58 delete fEngineTiming;
61 //-------------------
62 // Client management
63 //-------------------
65 int JackEngine::Open()
67 JackLog("JackEngine::Open\n");
69 // Open audio thread => request thread communication channel
70 if (fChannel->Open() < 0) {
71 jack_error("Cannot connect to server");
72 return -1;
73 } else {
74 return 0;
78 int JackEngine::Close()
80 JackLog("JackEngine::Close\n");
81 fChannel->Close();
83 // Close (possibly) remaining clients (RT is stopped)
84 for (int i = 0; i < CLIENT_NUM; i++) {
85 JackClientInterface* client = fClientTable[i];
86 if (client) {
87 JackLog("JackEngine::Close remaining client %ld\n", i);
88 ClientCloseAux(i, client, false);
89 client->Close();
90 delete client;
94 return 0;
97 int JackEngine::Allocate()
99 for (int i = 0; i < CLIENT_NUM; i++) {
100 if (!fClientTable[i]) {
101 JackLog("JackEngine::AllocateRefNum ref = %ld\n", i);
102 return i;
106 return -1;
109 //------------------
110 // Graph management
111 //------------------
113 bool JackEngine::Process(jack_time_t callback_usecs)
115 // Transport
116 fEngineControl->fTransport.CycleBegin(fEngineControl->fSampleRate, callback_usecs);
117 bool res = true;
119 // Timing
120 fEngineControl->fFrameTimer.IncFrameTime(fEngineControl->fBufferSize, callback_usecs, fEngineControl->fPeriodUsecs);
121 fEngineTiming->UpdateTiming(callback_usecs);
123 // Graph
124 if (fGraphManager->IsFinishedGraph()) {
125 fLastSwitchUsecs = callback_usecs;
126 if (fGraphManager->RunNextGraph()) // True if the graph actually switched to a new state
127 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0);
128 fSignal->SignalAll(); // Signal for threads waiting for next cycle
129 res = true;
130 } else {
131 JackLog("Process: graph not finished!\n");
132 if (callback_usecs > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
133 JackLog("Process: switch to next state %ld\n", long(callback_usecs - fLastSwitchUsecs));
134 //RemoveZombifiedClients(callback_usecs); TODO
135 fLastSwitchUsecs = callback_usecs;
136 if (fGraphManager->RunNextGraph())
137 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0);
138 fSignal->SignalAll(); // Signal for threads waiting for next cycle
139 res = true;
140 } else {
141 JackLog("Process: waiting to switch %ld\n", long(callback_usecs - fLastSwitchUsecs));
142 if (callback_usecs < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failling cycle
143 CheckXRun(callback_usecs);
144 fGraphManager->RunCurrentGraph();
145 res = false;
149 // Transport
150 fEngineControl->fTransport.CycleEnd(fClientTable, fEngineControl->fSampleRate, fEngineControl->fBufferSize);
151 return res;
155 Client that finish *after* the callback date are considered late even if their output buffers may have been
156 correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
159 void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions de fin
161 for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
162 JackClientInterface* client = fClientTable[i];
163 if (client && client->GetClientControl()->fActive) {
164 JackClientTiming* timing = fGraphManager->GetClientTiming(i);
165 jack_client_state_t status = timing->fStatus;
166 jack_time_t finished_date = timing->fFinishedAt;
168 if (status != NotTriggered && status != Finished) {
169 jack_error("JackEngine::XRun: client = %s was not runned: state = %ld", client->GetClientControl()->fName, status);
170 //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
171 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients
173 if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
174 jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
175 //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
176 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients
182 //---------------
183 // Zombification
184 //---------------
186 bool JackEngine::IsZombie(JackClientInterface* client, jack_time_t current_time)
188 return ((current_time - fGraphManager->GetClientTiming(client->GetClientControl()->fRefNum)->fFinishedAt) > 2 * fEngineControl->fTimeOutUsecs); // A VERIFIER
191 // TODO : check what happens with looped sub-graph....
192 void JackEngine::GetZombifiedClients(bool zombi_clients[CLIENT_NUM], jack_time_t current_time)
194 for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
195 JackClientInterface* client1 = fClientTable[i];
196 if (client1 && IsZombie(client1, current_time)) {
197 JackLog("JackEngine::GetZombifiedClients: %s\n", client1->GetClientControl()->fName);
198 zombi_clients[i] = true; // Assume client is dead
199 // If another dead client is connected to the scanned one, then the scanned one is not the first of the dead subgraph
200 for (int j = REAL_REFNUM; j < CLIENT_NUM; j++) {
201 JackClientInterface* client2 = fClientTable[j];
202 if (client2 && IsZombie(client2, current_time) && fGraphManager->IsDirectConnection(j, i)) {
203 zombi_clients[i] = false;
204 break;
207 } else {
208 zombi_clients[i] = false;
213 void JackEngine::RemoveZombifiedClients(jack_time_t current_time)
215 bool zombi_clients[CLIENT_NUM];
216 GetZombifiedClients(zombi_clients, current_time);
218 for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
219 if (zombi_clients[i] && !fClientTable[i]->GetClientControl()->fZombie) {
220 fClientTable[i]->GetClientControl()->fZombie = true;
221 JackLog("RemoveZombifiedCients: name = %s\n", fClientTable[i]->GetClientControl()->fName);
222 fGraphManager->DirectDisconnect(FREEWHEEL_DRIVER_REFNUM, i);
223 fGraphManager->DirectDisconnect(i, FREEWHEEL_DRIVER_REFNUM);
224 fGraphManager->DisconnectAllPorts(i);
225 fChannel->ClientNotify(i, JackNotifyChannelInterface::kZombifyClient, 0); // Signal engine
230 void JackEngine::ZombifyClient(int refnum)
232 NotifyClient(refnum, JackNotifyChannelInterface::kZombifyClient, false, 0);
235 //---------------
236 // Notifications
237 //---------------
239 void JackEngine::NotifyClient(int refnum, int event, int sync, int value)
241 JackClientInterface* client = fClientTable[refnum];
242 // The client may be notified by the RT thread while closing
243 if (client && (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, value) < 0)) {
244 jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value);
245 } else {
246 JackLog("JackEngine::NotifyClient: client not available anymore\n");
250 void JackEngine::NotifyClients(int event, int sync, int value)
252 for (int i = 0; i < CLIENT_NUM; i++) {
253 JackClientInterface* client = fClientTable[i];
254 if (client && (client->ClientNotify(i, client->GetClientControl()->fName, event, sync, value) < 0)) {
255 jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value);
260 int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
262 // Notify existing clients of the new client and new client of existing clients.
263 for (int i = 0; i < CLIENT_NUM; i++) {
264 JackClientInterface* old_client = fClientTable[i];
265 if (old_client) {
266 if (old_client->ClientNotify(refnum, name, JackNotifyChannelInterface::kAddClient, true, 0) < 0)
267 return -1;
268 if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, JackNotifyChannelInterface::kAddClient, true, 0) < 0)
269 return -1;
273 return 0;
276 void JackEngine::NotifyRemoveClient(const char* name, int refnum)
278 // Notify existing clients (including the one beeing suppressed) of the removed client
279 for (int i = 0; i < CLIENT_NUM; i++) {
280 JackClientInterface* client = fClientTable[i];
281 if (client) {
282 client->ClientNotify(refnum, name, JackNotifyChannelInterface::kRemoveClient, true, 0);
287 // Coming from the driver
288 void JackEngine::NotifyXRun(jack_time_t callback_usecs)
290 // Use the audio thread => request thread communication channel
291 fEngineControl->fFrameTimer.ResetFrameTime(fEngineControl->fSampleRate, callback_usecs, fEngineControl->fPeriodUsecs);
292 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0);
295 void JackEngine::NotifyXRun(int refnum)
297 if (refnum == ALL_CLIENTS) {
298 NotifyClients(JackNotifyChannelInterface::kXRunCallback, false, 0);
299 } else {
300 NotifyClient(refnum, JackNotifyChannelInterface::kXRunCallback, false, 0);
304 void JackEngine::NotifyGraphReorder()
306 NotifyClients(JackNotifyChannelInterface::kGraphOrderCallback, false, 0);
309 void JackEngine::NotifyBufferSize(jack_nframes_t nframes)
311 NotifyClients(JackNotifyChannelInterface::kBufferSizeCallback, true, nframes);
314 void JackEngine::NotifyFreewheel(bool onoff)
316 fEngineControl->fRealTime = !onoff;
317 NotifyClients((onoff ? JackNotifyChannelInterface::kStartFreewheel : JackNotifyChannelInterface::kStopFreewheel), true, 0);
320 void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
322 NotifyClients((onoff ? JackNotifyChannelInterface::kPortRegistrationOn : JackNotifyChannelInterface::kPortRegistrationOff), false, port_index);
325 //-------------------
326 // Client management
327 //-------------------
329 bool JackEngine::ClientCheckName(const char* name)
331 for (int i = 0; i < CLIENT_NUM; i++) {
332 JackClientInterface* client = fClientTable[i];
333 if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
334 return true;
337 return false;
340 // Used for external clients
341 int JackEngine::ClientNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
343 if (ClientCheckName(name)) {
344 jack_error("client %s already registered", name);
345 return -1;
348 JackExternalClient* client = new JackExternalClient();
349 if (ClientExternalNew(name, ref, shared_engine, shared_client, shared_graph_manager, client) < 0) {
350 delete client;
351 return -1;
354 return 0;
357 // Used for external clients
358 int JackEngine::ClientExternalNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager, JackExternalClient* client)
360 JackLog("JackEngine::ClientNew: name = %s \n", name);
361 int refnum = Allocate();
363 if (refnum < 0) {
364 jack_error("No more refnum available");
365 return -1;
368 if (!fSynchroTable[refnum]->Allocate(name, 0)) {
369 jack_error("Cannot allocate synchro");
370 goto error;
373 if (client->Open(name, refnum, shared_client) < 0) {
374 jack_error("Cannot open client");
375 goto error;
378 if (!fSignal->TimedWait(5 * 1000000)) {
379 // Failure if RT thread is not running (problem with the driver...)
380 jack_error("Driver is not running");
381 goto error;
384 if (NotifyAddClient(client, name, refnum) < 0) {
385 jack_error("Cannot notify add client");
386 goto error;
389 fClientTable[refnum] = client;
390 fGraphManager->InitRefNum(refnum);
391 fEngineTiming->ResetRollingUsecs();
392 *shared_engine = fEngineControl->GetShmIndex();
393 *shared_graph_manager = fGraphManager->GetShmIndex();
394 *ref = refnum;
395 return 0;
397 error:
398 ClientCloseAux(refnum, client, false);
399 client->Close();
400 return -1;
403 // Used for server driver clients
404 int JackEngine::ClientInternalNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client)
406 JackLog("JackEngine::ClientInternalNew: name = %s\n", name);
407 int refnum = Allocate();
409 if (refnum < 0) {
410 jack_error("No more refnum available");
411 return -1;
414 if (!fSynchroTable[refnum]->Allocate(name, 0)) {
415 jack_error("Cannot allocate synchro");
416 return -1;
419 if (NotifyAddClient(client, name, refnum) < 0) {
420 jack_error("Cannot notify add client");
421 return -1;
424 fClientTable[refnum] = client;
425 fGraphManager->InitRefNum(refnum);
426 fEngineTiming->ResetRollingUsecs();
427 *shared_engine = fEngineControl;
428 *shared_manager = fGraphManager;
429 *ref = refnum;
430 return 0;
433 // Used for externall clients
434 int JackEngine::ClientClose(int refnum)
436 JackClientInterface* client = fClientTable[refnum];
437 if (client) {
438 fEngineControl->fTransport.ResetTimebase(refnum);
439 int res = ClientCloseAux(refnum, client, true);
440 client->Close();
441 delete client;
442 return res;
443 } else {
444 return -1;
448 // Used for server internal clients
449 int JackEngine::ClientInternalClose(int refnum)
451 JackClientInterface* client = fClientTable[refnum];
452 return (client) ? ClientCloseAux(refnum, client, true) : -1;
455 // Used for drivers that close when the RT thread is stopped
456 int JackEngine::ClientInternalCloseIm(int refnum)
458 JackClientInterface* client = fClientTable[refnum];
459 return (client) ? ClientCloseAux(refnum, client, false) : -1;
462 int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
464 JackLog("JackEngine::ClientCloseAux ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
466 // Remove the client from the table
467 fClientTable[refnum] = NULL;
469 // Remove ports
470 fGraphManager->RemoveAllPorts(refnum);
472 // Wait until next cycle to be sure client is not used anymore
473 if (wait) {
474 if (!fSignal->TimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
475 JackLog("JackEngine::ClientCloseAux wait error ref = %ld \n", refnum);
479 // Notify running clients
480 NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
482 // Cleanup...
483 fSynchroTable[refnum]->Destroy();
484 fEngineTiming->ResetRollingUsecs();
485 return 0;
488 int JackEngine::ClientActivate(int refnum)
490 JackClientInterface* client = fClientTable[refnum];
491 assert(fClientTable[refnum]);
493 JackLog("JackEngine::ClientActivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
494 // Wait for graph state change to be effective
495 if (!fSignal->TimedWait(fEngineControl->fPeriodUsecs * 10)) {
496 JackLog("JackEngine::ClientActivate wait error ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
497 return -1;
498 } else {
499 return 0;
503 // May be called without client
504 int JackEngine::ClientDeactivate(int refnum)
506 JackClientInterface* client = fClientTable[refnum];
507 if (client == NULL)
508 return -1;
510 JackLog("JackEngine::ClientDeactivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
511 fGraphManager->DisconnectAllPorts(refnum);
512 // Wait for graph state change to be effective
513 if (!fSignal->TimedWait(fEngineControl->fPeriodUsecs * 10)) {
514 JackLog("JackEngine::ClientDeactivate wait error ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
515 return -1;
516 } else {
517 return 0;
521 //-----------------
522 // Port management
523 //-----------------
525 int JackEngine::PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
527 JackLog("JackEngine::PortRegister ref = %ld name = %s flags = %d buffer_size = %d\n", refnum, name, flags, buffer_size);
528 assert(fClientTable[refnum]);
530 *port_index = fGraphManager->AllocatePort(refnum, name, (JackPortFlags)flags);
531 if (*port_index != NO_PORT) {
532 NotifyPortRegistation(*port_index, true);
533 return 0;
534 } else {
535 return -1;
539 int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
541 JackLog("JackEngine::PortUnRegister ref = %ld port_index = %ld\n", refnum, port_index);
542 assert(fClientTable[refnum]);
544 if (fGraphManager->RemovePort(refnum, port_index) == 0) {
545 fGraphManager->ReleasePort(port_index);
546 NotifyPortRegistation(port_index, false);
547 return 0;
548 } else {
549 return -1;
553 int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
555 JackLog("JackEngine::PortConnect src = %s dst = %s\n", src, dst);
556 jack_port_id_t port_src, port_dst;
558 return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
559 ? -1
560 : PortConnect(refnum, port_src, port_dst);
563 int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
565 JackLog("JackEngine::PortDisconnect src = %s dst = %s\n", src, dst);
566 jack_port_id_t port_src, port_dst;
568 return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
569 ? -1
570 : fGraphManager->Disconnect(port_src, port_dst);
573 int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
575 JackLog("JackEngine::PortConnect src = %d dst = %d\n", src, dst);
576 JackClientInterface* client;
577 int ref;
579 if (fGraphManager->CheckPorts(src, dst) < 0)
580 return -1;
582 ref = fGraphManager->GetOutputRefNum(src);
583 assert(ref >= 0);
584 client = fClientTable[ref];
585 assert(client);
586 if (!client->GetClientControl()->fActive) {
587 jack_error("Cannot connect ports owned by inactive clients:"
588 " \"%s\" is not active", client->GetClientControl()->fName);
589 return -1;
592 ref = fGraphManager->GetInputRefNum(dst);
593 assert(ref >= 0);
594 client = fClientTable[ref];
595 assert(client);
596 if (!client->GetClientControl()->fActive) {
597 jack_error("Cannot connect ports owned by inactive clients:"
598 " \"%s\" is not active", client->GetClientControl()->fName);
599 return -1;
602 return fGraphManager->Connect(src, dst);
605 int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
607 JackLog("JackEngine::PortDisconnect src = %d dst = %d\n", src, dst);
609 if (dst == ALL_PORTS) {
610 return (fGraphManager->CheckPort(src) < 0)
611 ? -1
612 : fGraphManager->DisconnectAll(src);
613 } else {
614 return (fGraphManager->CheckPorts(src, dst) < 0)
615 ? -1
616 : fGraphManager->Disconnect(src, dst);
620 //----------------------
621 // Transport management
622 //----------------------
624 int JackEngine::ReleaseTimebase(int refnum)
626 return fEngineControl->fTransport.ResetTimebase(refnum);
629 int JackEngine::SetTimebaseCallback(int refnum, int conditional)
631 return fEngineControl->fTransport.SetTimebase(refnum, conditional);
634 //-----------
635 // Debugging
636 //-----------
638 void JackEngine::PrintState()
640 std::cout << "Engine State" << std::endl;
642 for (int i = 0; i < CLIENT_NUM; i++) {
643 JackClientInterface* client = fClientTable[i];
644 if (client)
645 std::cout << "Client : " << client->GetClientControl()->fName << " : " << i << std::endl;
648 //fGraphManager->PrintState();
649 fEngineTiming->PrintState();
652 } // end of namespace