Cleanup
[jack2.git] / common / JackEngine.cpp
blob607ca29c3d2261abe5086862f01a978bcdb3561e
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,
37 JackSynchro** table,
38 JackEngineControl* control)
40 fGraphManager = manager;
41 fSynchroTable = table;
42 fEngineControl = control;
43 fChannel = JackGlobals::MakeServerNotifyChannel();
44 fSignal = JackGlobals::MakeInterProcessSync();
45 fEngineTiming = new JackEngineTiming(fClientTable, fGraphManager, fEngineControl);
46 for (int i = 0; i < CLIENT_NUM; i++)
47 fClientTable[i] = NULL;
48 fEngineTiming->ClearTimeMeasures();
49 fEngineTiming->ResetRollingUsecs();
52 JackEngine::~JackEngine()
54 delete fChannel;
55 delete fEngineTiming;
56 delete fSignal;
59 //-------------------
60 // Client management
61 //-------------------
63 int JackEngine::Open()
65 JackLog("JackEngine::Open\n");
67 // Open audio thread => request thread communication channel
68 if (fChannel->Open() < 0) {
69 jack_error("Cannot connect to server");
70 return -1;
71 } else {
72 return 0;
76 int JackEngine::Close()
78 JackLog("JackEngine::Close\n");
79 fChannel->Close();
81 // Close (possibly) remaining clients (RT is stopped)
82 for (int i = 0; i < CLIENT_NUM; i++) {
83 JackClientInterface* client = fClientTable[i];
84 if (client) {
85 JackLog("JackEngine::Close remaining client %ld\n", i);
86 ClientCloseAux(i, client, false);
87 client->Close();
88 delete client;
92 fSignal->Destroy();
93 return 0;
96 int JackEngine::Allocate()
98 for (int i = 0; i < CLIENT_NUM; i++) {
99 if (!fClientTable[i]) {
100 JackLog("JackEngine::AllocateRefNum ref = %ld\n", i);
101 return i;
105 return -1;
108 //------------------
109 // Graph management
110 //------------------
112 void JackEngine::ProcessNext(jack_time_t callback_usecs)
114 fLastSwitchUsecs = callback_usecs;
115 if (fGraphManager->RunNextGraph()) // True if the graph actually switched to a new state
116 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0);
117 fSignal->SignalAll(); // Signal for threads waiting for next cycle
120 void JackEngine::ProcessCurrent(jack_time_t callback_usecs)
122 if (callback_usecs < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failling cycle
123 CheckXRun(callback_usecs);
124 fGraphManager->RunCurrentGraph();
127 bool JackEngine::Process(jack_time_t callback_usecs)
129 bool res = true;
131 // Transport begin
132 fEngineControl->CycleBegin(callback_usecs);
134 // Timing
135 fEngineControl->IncFrameTime(callback_usecs);
136 fEngineTiming->UpdateTiming(callback_usecs);
138 // Graph
139 if (fGraphManager->IsFinishedGraph()) {
140 ProcessNext(callback_usecs);
141 res = true;
142 } else {
143 JackLog("Process: graph not finished!\n");
144 if (callback_usecs > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
145 JackLog("Process: switch to next state delta = %ld\n", long(callback_usecs - fLastSwitchUsecs));
146 //RemoveZombifiedClients(callback_usecs); TODO
147 ProcessNext(callback_usecs);
148 res = true;
149 } else {
150 JackLog("Process: waiting to switch delta = %ld\n", long(callback_usecs - fLastSwitchUsecs));
151 ProcessCurrent(callback_usecs);
152 res = false;
156 // Transport end
157 fEngineControl->CycleEnd(fClientTable);
158 return res;
163 Client that finish *after* the callback date are considered late even if their output buffers may have been
164 correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
167 void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions de fin
169 for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
170 JackClientInterface* client = fClientTable[i];
171 if (client && client->GetClientControl()->fActive) {
172 JackClientTiming* timing = fGraphManager->GetClientTiming(i);
173 jack_client_state_t status = timing->fStatus;
174 jack_time_t finished_date = timing->fFinishedAt;
176 if (status != NotTriggered && status != Finished) {
177 jack_error("JackEngine::XRun: client = %s was not run: state = %ld", client->GetClientControl()->fName, status);
178 //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
179 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients
181 if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
182 jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
183 //fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
184 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients
190 //---------------
191 // Zombification
192 //---------------
194 bool JackEngine::IsZombie(JackClientInterface* client, jack_time_t current_time)
196 return ((current_time - fGraphManager->GetClientTiming(client->GetClientControl()->fRefNum)->fFinishedAt) > 2 * fEngineControl->fTimeOutUsecs); // A VERIFIER
199 // TODO : check what happens with looped sub-graph....
200 void JackEngine::GetZombifiedClients(bool zombi_clients[CLIENT_NUM], jack_time_t current_time)
202 for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
203 JackClientInterface* client1 = fClientTable[i];
204 if (client1 && IsZombie(client1, current_time)) {
205 JackLog("JackEngine::GetZombifiedClients: %s\n", client1->GetClientControl()->fName);
206 zombi_clients[i] = true; // Assume client is dead
207 // If another dead client is connected to the scanned one, then the scanned one is not the first of the dead subgraph
208 for (int j = REAL_REFNUM; j < CLIENT_NUM; j++) {
209 JackClientInterface* client2 = fClientTable[j];
210 if (client2 && IsZombie(client2, current_time) && fGraphManager->IsDirectConnection(j, i)) {
211 zombi_clients[i] = false;
212 break;
215 } else {
216 zombi_clients[i] = false;
221 void JackEngine::RemoveZombifiedClients(jack_time_t current_time)
223 bool zombi_clients[CLIENT_NUM];
224 GetZombifiedClients(zombi_clients, current_time);
226 for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
227 if (zombi_clients[i] && !fClientTable[i]->GetClientControl()->fZombie) {
228 fClientTable[i]->GetClientControl()->fZombie = true;
229 JackLog("RemoveZombifiedCients: name = %s\n", fClientTable[i]->GetClientControl()->fName);
230 fGraphManager->DirectDisconnect(FREEWHEEL_DRIVER_REFNUM, i);
231 fGraphManager->DirectDisconnect(i, FREEWHEEL_DRIVER_REFNUM);
232 fGraphManager->DisconnectAllPorts(i);
233 fChannel->ClientNotify(i, JackNotifyChannelInterface::kZombifyClient, 0); // Signal engine
238 void JackEngine::ZombifyClient(int refnum)
240 NotifyClient(refnum, JackNotifyChannelInterface::kZombifyClient, false, 0);
243 //---------------
244 // Notifications
245 //---------------
247 void JackEngine::NotifyClient(int refnum, int event, int sync, int value)
249 JackClientInterface* client = fClientTable[refnum];
250 // The client may be notified by the RT thread while closing
251 if (client) {
252 if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, value) < 0)
253 jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value);
254 } else {
255 JackLog("JackEngine::NotifyClient: client not available anymore\n");
259 void JackEngine::NotifyClients(int event, int sync, int value)
261 for (int i = 0; i < CLIENT_NUM; i++) {
262 JackClientInterface* client = fClientTable[i];
263 if (client && (client->ClientNotify(i, client->GetClientControl()->fName, event, sync, value) < 0)) {
264 jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value);
269 int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
271 // Notify existing clients of the new client and new client of existing clients.
272 for (int i = 0; i < CLIENT_NUM; i++) {
273 JackClientInterface* old_client = fClientTable[i];
274 if (old_client) {
275 if (old_client->ClientNotify(refnum, name, JackNotifyChannelInterface::kAddClient, true, 0) < 0) {
276 jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName);
277 return -1;
279 if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, JackNotifyChannelInterface::kAddClient, true, 0) < 0) {
280 jack_error("NotifyAddClient new_client fails name = %s", name);
281 return -1;
286 return 0;
289 void JackEngine::NotifyRemoveClient(const char* name, int refnum)
291 // Notify existing clients (including the one beeing suppressed) of the removed client
292 for (int i = 0; i < CLIENT_NUM; i++) {
293 JackClientInterface* client = fClientTable[i];
294 if (client) {
295 client->ClientNotify(refnum, name, JackNotifyChannelInterface::kRemoveClient, true, 0);
300 // Coming from the driver
301 void JackEngine::NotifyXRun(jack_time_t callback_usecs)
303 // Use the audio thread => request thread communication channel
304 fEngineControl->ResetFrameTime(callback_usecs);
305 fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0);
308 void JackEngine::NotifyXRun(int refnum)
310 if (refnum == ALL_CLIENTS) {
311 NotifyClients(JackNotifyChannelInterface::kXRunCallback, false, 0);
312 } else {
313 NotifyClient(refnum, JackNotifyChannelInterface::kXRunCallback, false, 0);
317 void JackEngine::NotifyGraphReorder()
319 NotifyClients(JackNotifyChannelInterface::kGraphOrderCallback, false, 0);
322 void JackEngine::NotifyBufferSize(jack_nframes_t nframes)
324 NotifyClients(JackNotifyChannelInterface::kBufferSizeCallback, true, nframes);
327 void JackEngine::NotifyFreewheel(bool onoff)
329 fEngineControl->fRealTime = !onoff;
330 NotifyClients((onoff ? JackNotifyChannelInterface::kStartFreewheel : JackNotifyChannelInterface::kStopFreewheel), true, 0);
333 void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
335 NotifyClients((onoff ? JackNotifyChannelInterface::kPortRegistrationOn : JackNotifyChannelInterface::kPortRegistrationOff), false, port_index);
338 void JackEngine::NotifyActivate(int refnum)
340 NotifyClient(refnum, JackNotifyChannelInterface::kActivateClient, true, 0);
343 //-------------------
344 // Client management
345 //-------------------
347 bool JackEngine::ClientCheckName(const char* name)
349 for (int i = 0; i < CLIENT_NUM; i++) {
350 JackClientInterface* client = fClientTable[i];
351 if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
352 return true;
355 return false;
358 // Used for external clients
359 int JackEngine::ClientExternalOpen(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
361 JackLog("JackEngine::ClientOpen: name = %s \n", name);
363 if (ClientCheckName(name)) {
364 jack_error("client %s already registered", name);
365 return -1;
368 int refnum = Allocate();
369 if (refnum < 0) {
370 jack_error("No more refnum available");
371 return -1;
374 JackExternalClient* client = new JackExternalClient();
376 if (!fSynchroTable[refnum]->Allocate(name, 0)) {
377 jack_error("Cannot allocate synchro");
378 goto error;
381 if (client->Open(name, refnum, shared_client) < 0) {
382 jack_error("Cannot open client");
383 goto error;
386 if (!fSignal->TimedWait(5 * 1000000)) {
387 // Failure if RT thread is not running (problem with the driver...)
388 jack_error("Driver is not running");
389 goto error;
392 if (NotifyAddClient(client, name, refnum) < 0) {
393 jack_error("Cannot notify add client");
394 goto error;
397 fClientTable[refnum] = client;
398 fGraphManager->InitRefNum(refnum);
399 fEngineTiming->ResetRollingUsecs();
400 *shared_engine = fEngineControl->GetShmIndex();
401 *shared_graph_manager = fGraphManager->GetShmIndex();
402 *ref = refnum;
403 return 0;
405 error:
406 ClientCloseAux(refnum, client, false);
407 client->Close();
408 delete client;
409 return -1;
412 // Used for server driver clients
413 int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client)
415 JackLog("JackEngine::ClientInternalNew: name = %s\n", name);
417 if (ClientCheckName(name)) {
418 jack_error("client %s already registered", name);
419 return -1;
422 int refnum = Allocate();
423 if (refnum < 0) {
424 jack_error("No more refnum available");
425 return -1;
428 if (!fSynchroTable[refnum]->Allocate(name, 0)) {
429 jack_error("Cannot allocate synchro");
430 return -1;
433 if (NotifyAddClient(client, name, refnum) < 0) {
434 jack_error("Cannot notify add client");
435 return -1;
438 fClientTable[refnum] = client;
439 fGraphManager->InitRefNum(refnum);
440 fEngineTiming->ResetRollingUsecs();
441 *shared_engine = fEngineControl;
442 *shared_manager = fGraphManager;
443 *ref = refnum;
444 return 0;
447 // Used for externall clients
448 int JackEngine::ClientExternalClose(int refnum)
450 JackClientInterface* client = fClientTable[refnum];
451 if (client) {
452 fEngineControl->fTransport.ResetTimebase(refnum);
453 int res = ClientCloseAux(refnum, client, true);
454 client->Close();
455 delete client;
456 return res;
457 } else {
458 return -1;
462 // Used for server internal clients
463 int JackEngine::ClientInternalClose(int refnum)
465 JackClientInterface* client = fClientTable[refnum];
466 return (client) ? ClientCloseAux(refnum, client, true) : -1;
469 // Used for drivers that close when the RT thread is stopped
470 int JackEngine::ClientInternalCloseIm(int refnum)
472 JackClientInterface* client = fClientTable[refnum];
473 return (client) ? ClientCloseAux(refnum, client, false) : -1;
476 int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
478 JackLog("JackEngine::ClientCloseAux ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
480 // Remove the client from the table
481 fClientTable[refnum] = NULL;
483 // Remove ports
484 fGraphManager->RemoveAllPorts(refnum);
486 // Wait until next cycle to be sure client is not used anymore
487 if (wait) {
488 if (!fSignal->TimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
489 jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum);
493 // Notify running clients
494 NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
496 // Cleanup...
497 fSynchroTable[refnum]->Destroy();
498 fEngineTiming->ResetRollingUsecs();
499 return 0;
502 int JackEngine::ClientActivate(int refnum)
504 JackClientInterface* client = fClientTable[refnum];
505 assert(fClientTable[refnum]);
507 JackLog("JackEngine::ClientActivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
508 fGraphManager->Activate(refnum);
510 // Wait for graph state change to be effective
511 if (!fSignal->TimedWait(fEngineControl->fTimeOutUsecs * 10)) {
512 jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
513 return -1;
514 } else {
515 NotifyActivate(refnum);
516 return 0;
520 // May be called without client
521 int JackEngine::ClientDeactivate(int refnum)
523 JackClientInterface* client = fClientTable[refnum];
524 if (client == NULL)
525 return -1;
527 JackLog("JackEngine::ClientDeactivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
528 fGraphManager->Deactivate(refnum);
529 fLastSwitchUsecs = 0; // Force switch to occur next cycle, even when called with "dead" clients
531 // Wait for graph state change to be effective
532 if (!fSignal->TimedWait(fEngineControl->fTimeOutUsecs * 10)) {
533 jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
534 return -1;
535 } else {
536 return 0;
540 //-----------------
541 // Port management
542 //-----------------
544 int JackEngine::PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
546 JackLog("JackEngine::PortRegister ref = %ld name = %s flags = %d buffer_size = %d\n", refnum, name, flags, buffer_size);
547 assert(fClientTable[refnum]);
549 *port_index = fGraphManager->AllocatePort(refnum, name, (JackPortFlags)flags);
550 if (*port_index != NO_PORT) {
551 NotifyPortRegistation(*port_index, true);
552 return 0;
553 } else {
554 return -1;
558 int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
560 JackLog("JackEngine::PortUnRegister ref = %ld port_index = %ld\n", refnum, port_index);
561 assert(fClientTable[refnum]);
563 if (fGraphManager->RemovePort(refnum, port_index) == 0) {
564 fGraphManager->ReleasePort(port_index);
565 NotifyPortRegistation(port_index, false);
566 return 0;
567 } else {
568 return -1;
572 int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
574 JackLog("JackEngine::PortConnect src = %s dst = %s\n", src, dst);
575 jack_port_id_t port_src, port_dst;
577 return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
578 ? -1
579 : PortConnect(refnum, port_src, port_dst);
582 int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
584 JackLog("JackEngine::PortDisconnect src = %s dst = %s\n", src, dst);
585 jack_port_id_t port_src, port_dst;
587 return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
588 ? -1
589 : fGraphManager->Disconnect(port_src, port_dst);
592 int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
594 JackLog("JackEngine::PortConnect src = %d dst = %d\n", src, dst);
595 JackClientInterface* client;
596 int ref;
598 if (fGraphManager->CheckPorts(src, dst) < 0)
599 return -1;
601 ref = fGraphManager->GetOutputRefNum(src);
602 assert(ref >= 0);
603 client = fClientTable[ref];
604 assert(client);
605 if (!client->GetClientControl()->fActive) {
606 jack_error("Cannot connect ports owned by inactive clients:"
607 " \"%s\" is not active", client->GetClientControl()->fName);
608 return -1;
611 ref = fGraphManager->GetInputRefNum(dst);
612 assert(ref >= 0);
613 client = fClientTable[ref];
614 assert(client);
615 if (!client->GetClientControl()->fActive) {
616 jack_error("Cannot connect ports owned by inactive clients:"
617 " \"%s\" is not active", client->GetClientControl()->fName);
618 return -1;
621 return fGraphManager->Connect(src, dst);
624 int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
626 JackLog("JackEngine::PortDisconnect src = %d dst = %d\n", src, dst);
628 if (dst == ALL_PORTS) {
629 return (fGraphManager->CheckPort(src) < 0)
630 ? -1
631 : fGraphManager->DisconnectAll(src);
632 } else {
633 return (fGraphManager->CheckPorts(src, dst) < 0)
634 ? -1
635 : fGraphManager->Disconnect(src, dst);
639 //----------------------
640 // Transport management
641 //----------------------
643 int JackEngine::ReleaseTimebase(int refnum)
645 return fEngineControl->fTransport.ResetTimebase(refnum);
648 int JackEngine::SetTimebaseCallback(int refnum, int conditional)
650 return fEngineControl->fTransport.SetTimebase(refnum, conditional);
653 //-----------
654 // Debugging
655 //-----------
657 void JackEngine::PrintState()
659 std::cout << "Engine State" << std::endl;
661 for (int i = 0; i < CLIENT_NUM; i++) {
662 JackClientInterface* client = fClientTable[i];
663 if (client)
664 std::cout << "Client : " << client->GetClientControl()->fName << " : " << i << std::endl;
667 //fGraphManager->PrintState();
668 fEngineTiming->PrintState();
671 } // end of namespace