JackEngine.cpp 34 KB
Newer Older
sletz's avatar
sletz committed
1
/*
sletz's avatar
sletz committed
2
Copyright (C) 2004-2008 Grame
sletz's avatar
sletz committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <iostream>
#include <fstream>
#include <assert.h>

24
#include "JackSystemDeps.h"
sletz's avatar
sletz committed
25
#include "JackLockedEngine.h"
sletz's avatar
sletz committed
26
#include "JackExternalClient.h"
27
#include "JackInternalClient.h"
sletz's avatar
sletz committed
28
29
#include "JackEngineControl.h"
#include "JackClientControl.h"
30
#include "JackServerGlobals.h"
sletz's avatar
sletz committed
31
32
#include "JackGlobals.h"
#include "JackChannel.h"
sletz's avatar
sletz committed
33
#include "JackError.h"
sletz's avatar
sletz committed
34
35
36
37

namespace Jack
{

sletz's avatar
sletz committed
38
JackEngine::JackEngine(JackGraphManager* manager,
39
                       JackSynchro* table,
sletz's avatar
sletz committed
40
                       JackEngineControl* control)
sletz's avatar
sletz committed
41
42
43
44
45
{
    fGraphManager = manager;
    fSynchroTable = table;
    fEngineControl = control;
    for (int i = 0; i < CLIENT_NUM; i++)
46
        fClientTable[i] = NULL;
Stéphane Letz's avatar
Stéphane Letz committed
47
48
49
50
51
    fLastSwitchUsecs = 0;
    fMaxUUID = 0;
    fSessionPendingReplies = 0;
    fSessionTransaction = NULL;
    fSessionResult = NULL;
sletz's avatar
sletz committed
52
53
54
}

JackEngine::~JackEngine()
Stéphane Letz's avatar
Stéphane Letz committed
55
{}
sletz's avatar
sletz committed
56
57
58

int JackEngine::Open()
{
sletz's avatar
sletz committed
59
    jack_log("JackEngine::Open");
sletz's avatar
sletz committed
60
61

    // Open audio thread => request thread communication channel
62
    if (fChannel.Open(fEngineControl->fServerName) < 0) {
sletz's avatar
sletz committed
63
64
65
66
67
68
69
70
71
        jack_error("Cannot connect to server");
        return -1;
    } else {
        return 0;
    }
}

int JackEngine::Close()
{
sletz's avatar
sletz committed
72
    jack_log("JackEngine::Close");
73
    fChannel.Close();
sletz's avatar
sletz committed
74

75
    // Close remaining clients (RT is stopped)
76
    for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
77
78
79
        if (JackLoadableInternalClient* loadable_client = dynamic_cast<JackLoadableInternalClient*>(fClientTable[i])) {
            jack_log("JackEngine::Close loadable client = %s", loadable_client->GetClientControl()->fName);
            loadable_client->Close();
sletz's avatar
sletz committed
80
81
            // Close does not delete the pointer for internal clients
            fClientTable[i] = NULL;
sletz's avatar
sletz committed
82
83
84
85
            delete loadable_client;
        } else if (JackExternalClient* external_client = dynamic_cast<JackExternalClient*>(fClientTable[i])) {
            jack_log("JackEngine::Close external client = %s", external_client->GetClientControl()->fName);
            external_client->Close();
sletz's avatar
sletz committed
86
87
            // Close deletes the pointer for external clients
            fClientTable[i] = NULL;
sletz's avatar
sletz committed
88
89
        }
    }
sletz's avatar
sletz committed
90

sletz's avatar
sletz committed
91
92
    return 0;
}
93

94
95
96
97
void JackEngine::NotifyQuit()
{
    fChannel.NotifyQuit();
}
sletz's avatar
sletz committed
98

sletz's avatar
Cleanup    
sletz committed
99
100
101
102
//-----------------------------
// Client ressource management
//-----------------------------

sletz's avatar
sletz committed
103
int JackEngine::AllocateRefnum()
104
105
106
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        if (!fClientTable[i]) {
sletz's avatar
sletz committed
107
            jack_log("JackEngine::AllocateRefNum ref = %ld", i);
108
109
110
111
112
113
            return i;
        }
    }
    return -1;
}

sletz's avatar
sletz committed
114
115
void JackEngine::ReleaseRefnum(int ref)
{
sletz's avatar
sletz committed
116
117
118
119
    fClientTable[ref] = NULL;

    if (fEngineControl->fTemporary) {
        int i;
120
        for (i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
121
122
123
124
125
            if (fClientTable[i])
                break;
        }
        if (i == CLIENT_NUM) {
            // last client and temporay case: quit the server
sletz's avatar
sletz committed
126
            jack_log("JackEngine::ReleaseRefnum server quit");
sletz's avatar
sletz committed
127
            fEngineControl->fTemporary = false;
128
            throw JackTemporaryException();
sletz's avatar
sletz committed
129
130
        }
    }
sletz's avatar
sletz committed
131
132
}

sletz's avatar
sletz committed
133
134
135
136
//------------------
// Graph management
//------------------

sletz's avatar
sletz committed
137
void JackEngine::ProcessNext(jack_time_t cur_cycle_begin)
sletz's avatar
sletz committed
138
{
sletz's avatar
sletz committed
139
    fLastSwitchUsecs = cur_cycle_begin;
140
    if (fGraphManager->RunNextGraph())  { // True if the graph actually switched to a new state
141
142
        fChannel.Notify(ALL_CLIENTS, kGraphOrderCallback, 0);
        //NotifyGraphReorder();
143
    }
144
    fSignal.Signal();                   // Signal for threads waiting for next cycle
sletz's avatar
sletz committed
145
146
}

sletz's avatar
sletz committed
147
void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin)
sletz's avatar
sletz committed
148
{
sletz's avatar
sletz committed
149
150
    if (cur_cycle_begin < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failing cycle
        CheckXRun(cur_cycle_begin);
sletz's avatar
sletz committed
151
    fGraphManager->RunCurrentGraph();
sletz's avatar
sletz committed
152
153
}

sletz's avatar
sletz committed
154
bool JackEngine::Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end)
sletz's avatar
sletz committed
155
{
sletz's avatar
sletz committed
156
    bool res = true;
sletz's avatar
sletz committed
157

158
    // Cycle  begin
sletz's avatar
sletz committed
159
    fEngineControl->CycleBegin(fClientTable, fGraphManager, cur_cycle_begin, prev_cycle_end);
sletz's avatar
sletz committed
160

sletz's avatar
sletz committed
161
    // Graph
sletz's avatar
sletz committed
162
    if (fGraphManager->IsFinishedGraph()) {
sletz's avatar
sletz committed
163
        ProcessNext(cur_cycle_begin);
sletz's avatar
sletz committed
164
        res = true;
sletz's avatar
sletz committed
165
    } else {
sletz's avatar
sletz committed
166
        jack_log("Process: graph not finished!");
sletz's avatar
sletz committed
167
168
169
        if (cur_cycle_begin > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
            jack_log("Process: switch to next state delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
            ProcessNext(cur_cycle_begin);
sletz's avatar
sletz committed
170
            res = true;
sletz's avatar
sletz committed
171
        } else {
sletz's avatar
sletz committed
172
173
            jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
            ProcessCurrent(cur_cycle_begin);
sletz's avatar
sletz committed
174
175
            res = false;
        }
sletz's avatar
sletz committed
176
177
    }

178
    // Cycle end
sletz's avatar
sletz committed
179
180
    fEngineControl->CycleEnd(fClientTable);
    return res;
sletz's avatar
sletz committed
181
182
}

sletz's avatar
sletz committed
183
184
185
186
187
188
189
/*
Client that finish *after* the callback date are considered late even if their output buffers may have been
correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
*/

void JackEngine::CheckXRun(jack_time_t callback_usecs)  // REVOIR les conditions de fin
{
190
    for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
191
192
        JackClientInterface* client = fClientTable[i];
        if (client && client->GetClientControl()->fActive) {
sletz's avatar
sletz committed
193
194
            JackClientTiming* timing = fGraphManager->GetClientTiming(i);
            jack_client_state_t status = timing->fStatus;
sletz's avatar
sletz committed
195
            jack_time_t finished_date = timing->fFinishedAt;
sletz's avatar
sletz committed
196

sletz's avatar
sletz committed
197
            if (status != NotTriggered && status != Finished) {
sletz's avatar
Typo    
sletz committed
198
                jack_error("JackEngine::XRun: client = %s was not run: state = %ld", client->GetClientControl()->fName, status);
199
200
                fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);  // Notify all clients
                //NotifyXRun(ALL_CLIENTS);
sletz's avatar
sletz committed
201
            }
sletz's avatar
sletz committed
202

sletz's avatar
sletz committed
203
204
            if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
                jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
205
206
                fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);  // Notify all clients
                //NotifyXRun(ALL_CLIENTS);
sletz's avatar
sletz committed
207
208
209
210
211
            }
        }
    }
}

212
213
int JackEngine::ComputeTotalLatencies()
{
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    std::vector<jack_int_t> sorted;
    std::vector<jack_int_t>::iterator it;
    std::vector<jack_int_t>::reverse_iterator rit;

    fGraphManager->TopologicalSort(sorted);

    /* iterate over all clients in graph order, and emit
	 * capture latency callback.
	 */

    for (it = sorted.begin(); it != sorted.end(); it++) {
        jack_log("ComputeTotalLatencies %d", *it);
        NotifyClient(*it, kLatencyCallback, true, "", 0, 0);
    }

    /* now issue playback latency callbacks in reverse graph order
	 */
    for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) {
        NotifyClient(*rit, kLatencyCallback, true, "", 1, 0);
    }

235
236
237
    return 0;
}

sletz's avatar
sletz committed
238
239
240
241
//---------------
// Notifications
//---------------

242
void JackEngine::NotifyClient(int refnum, int event, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
243
244
{
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
245

sletz's avatar
sletz committed
246
    // The client may be notified by the RT thread while closing
247
    if (client) {
248

249
        if (client->GetClientControl()->fCallback[event]) {
250
251
252
253
254
255
256
257
            /*
                Important for internal clients : unlock before calling the notification callbacks.
            */
            bool res = fMutex.Unlock();
            if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, message, value1, value2) < 0)
                jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2);
            if (res)
                fMutex.Lock();
258

259
260
261
        } else {
            jack_log("JackEngine::NotifyClient: no callback for event = %ld", event);
        }
sletz's avatar
sletz committed
262
263
264
    }
}

265
void JackEngine::NotifyClients(int event, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
266
267
{
    for (int i = 0; i < CLIENT_NUM; i++) {
268
        NotifyClient(i, event, sync, message, value1, value2);
sletz's avatar
sletz committed
269
270
271
272
273
    }
}

int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
{
274
    jack_log("JackEngine::NotifyAddClient: name = %s", name);
sletz's avatar
sletz committed
275
276
277
278
    // Notify existing clients of the new client and new client of existing clients.
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* old_client = fClientTable[i];
        if (old_client) {
279
            if (old_client->ClientNotify(refnum, name, kAddClient, true, "", 0, 0) < 0) {
280
                jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName);
sletz's avatar
sletz committed
281
282
                return -1;
            }
283
            if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, kAddClient, true, "", 0, 0) < 0) {
284
                jack_error("NotifyAddClient new_client fails name = %s", name);
sletz's avatar
sletz committed
285
286
                return -1;
            }
sletz's avatar
sletz committed
287
288
289
290
291
292
293
294
295
296
297
298
        }
    }

    return 0;
}

void JackEngine::NotifyRemoveClient(const char* name, int refnum)
{
    // Notify existing clients (including the one beeing suppressed) of the removed client
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client) {
299
            client->ClientNotify(refnum, name, kRemoveClient, true, "",0, 0);
sletz's avatar
sletz committed
300
301
302
303
304
        }
    }
}

// Coming from the driver
305
void JackEngine::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs)
sletz's avatar
sletz committed
306
307
{
    // Use the audio thread => request thread communication channel
308
    fEngineControl->NotifyXRun(callback_usecs, delayed_usecs);
309
310
    fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);
    //NotifyXRun(ALL_CLIENTS);
sletz's avatar
sletz committed
311
312
313
314
315
}

void JackEngine::NotifyXRun(int refnum)
{
    if (refnum == ALL_CLIENTS) {
316
        NotifyClients(kXRunCallback, false, "", 0, 0);
sletz's avatar
sletz committed
317
    } else {
318
        NotifyClient(refnum, kXRunCallback, false, "", 0, 0);
sletz's avatar
sletz committed
319
320
321
322
323
    }
}

void JackEngine::NotifyGraphReorder()
{
324
    ComputeTotalLatencies();
325
    NotifyClients(kGraphOrderCallback, false, "", 0, 0);
sletz's avatar
sletz committed
326
327
}

328
void JackEngine::NotifyBufferSize(jack_nframes_t buffer_size)
sletz's avatar
sletz committed
329
{
330
    NotifyClients(kBufferSizeCallback, true, "", buffer_size, 0);
331
332
333
334
}

void JackEngine::NotifySampleRate(jack_nframes_t sample_rate)
{
335
    NotifyClients(kSampleRateCallback, true, "", sample_rate, 0);
sletz's avatar
sletz committed
336
337
}

338
339
340
341
void JackEngine::NotifyFailure(int code, const char* reason)
{
    NotifyClients(kShutDownCallback, false, reason, code, 0);
}
342

sletz's avatar
sletz committed
343
344
void JackEngine::NotifyFreewheel(bool onoff)
{
345
346
    if (onoff) {
        // Save RT state
347
        fEngineControl->fSavedRealTime = fEngineControl->fRealTime;
348
349
350
        fEngineControl->fRealTime = false;
    } else {
        // Restore RT state
351
352
        fEngineControl->fRealTime = fEngineControl->fSavedRealTime;
        fEngineControl->fSavedRealTime = false;
353
    }
354
    NotifyClients((onoff ? kStartFreewheelCallback : kStopFreewheelCallback), true, "", 0, 0);
sletz's avatar
sletz committed
355
356
357
358
}

void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
{
359
    NotifyClients((onoff ? kPortRegistrationOnCallback : kPortRegistrationOffCallback), false, "", port_index, 0);
sletz's avatar
sletz committed
360
361
}

362
void JackEngine::NotifyPortRename(jack_port_id_t port, const char* old_name)
363
{
364
    NotifyClients(kPortRenameCallback, false, old_name, port, 0);
365
366
}

sletz's avatar
sletz committed
367
368
void JackEngine::NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff)
{
369
    NotifyClients((onoff ? kPortConnectCallback : kPortDisconnectCallback), false, "", src, dst);
sletz's avatar
sletz committed
370
371
}

372
373
void JackEngine::NotifyActivate(int refnum)
{
374
    NotifyClient(refnum, kActivateClient, true, "", 0, 0);
375
376
}

377
378
379
380
//----------------------------
// Loadable client management
//----------------------------

381
int JackEngine::GetInternalClientName(int refnum, char* name_res)
382
{
sletz's avatar
sletz committed
383
    JackClientInterface* client = fClientTable[refnum];
384
385
    strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
    return 0;
386
387
388
389
}

int JackEngine::InternalClientHandle(const char* client_name, int* status, int* int_ref)
{
sletz's avatar
sletz committed
390
391
392
393
    // Clear status
    *status = 0;

    for (int i = 0; i < CLIENT_NUM; i++) {
394
395
        JackClientInterface* client = fClientTable[i];
        if (client && dynamic_cast<JackLoadableInternalClient*>(client) && (strcmp(client->GetClientControl()->fName, client_name) == 0)) {
sletz's avatar
sletz committed
396
            jack_log("InternalClientHandle found client name = %s ref = %ld",  client_name, i);
sletz's avatar
sletz committed
397
398
399
            *int_ref = i;
            return 0;
        }
400
    }
sletz's avatar
sletz committed
401
402
403

    *status |= (JackNoSuchClient | JackFailure);
    return -1;
404
405
406
407
}

int JackEngine::InternalClientUnload(int refnum, int* status)
{
sletz's avatar
sletz committed
408
409
410
411
412
413
414
415
    JackClientInterface* client = fClientTable[refnum];
    if (client) {
        int res = client->Close();
        delete client;
        *status = 0;
        return res;
    } else {
        *status = (JackNoSuchClient | JackFailure);
416
417
418
419
        return -1;
    }
}

sletz's avatar
sletz committed
420
421
422
423
//-------------------
// Client management
//-------------------

424
int JackEngine::ClientCheck(const char* name, int uuid, char* name_res, int protocol, int options, int* status)
425
{
sletz's avatar
sletz committed
426
427
428
429
    // Clear status
    *status = 0;
    strcpy(name_res, name);

sletz's avatar
sletz committed
430
    jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION);
sletz's avatar
sletz committed
431
432
433
434
435
436
437

    if (protocol != JACK_PROTOCOL_VERSION) {
        *status |= (JackFailure | JackVersionError);
        jack_error("JACK protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION);
        return -1;
    }

438
439
440
    std::map<int,std::string>::iterator res = fReservationMap.find(uuid);

    if (res != fReservationMap.end()) {
441
        strncpy(name_res, res->second.c_str(), JACK_CLIENT_NAME_SIZE);
442
    } else if (ClientCheckName(name)) {
sletz's avatar
sletz committed
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458

        *status |= JackNameNotUnique;

        if (options & JackUseExactName) {
            jack_error("cannot create new client; %s already exists", name);
            *status |= JackFailure;
            return -1;
        }

        if (GenerateUniqueName(name_res)) {
            *status |= JackFailure;
            return -1;
        }
    }

    return 0;
459
460
461
462
}

bool JackEngine::GenerateUniqueName(char* name)
{
sletz's avatar
sletz committed
463
464
465
466
467
    int tens, ones;
    int length = strlen(name);

    if (length > JACK_CLIENT_NAME_SIZE - 4) {
        jack_error("%s exists and is too long to make unique", name);
468
        return true;            /* failure */
sletz's avatar
sletz committed
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
    }

    /*  generate a unique name by appending "-01".."-99" */
    name[length++] = '-';
    tens = length++;
    ones = length++;
    name[tens] = '0';
    name[ones] = '1';
    name[length] = '\0';

    while (ClientCheckName(name)) {
        if (name[ones] == '9') {
            if (name[tens] == '9') {
                jack_error("client %s has 99 extra instances already", name);
                return true; /* give up */
            }
            name[tens]++;
            name[ones] = '0';
        } else {
            name[ones]++;
        }
    }
    return false;
492
493
}

sletz's avatar
sletz committed
494
495
496
497
498
499
500
501
bool JackEngine::ClientCheckName(const char* name)
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
            return true;
    }

502
    for (std::map<int,std::string>::iterator i = fReservationMap.begin(); i != fReservationMap.end(); i++) {
503
504
        if (i->second == name)
            return true;
505
506
    }

sletz's avatar
sletz committed
507
508
509
    return false;
}

510
511
512
513
514
515
516
517
int JackEngine::GetNewUUID()
{
    return fMaxUUID++;
}

void JackEngine::EnsureUUID(int uuid)
{
    if (uuid > fMaxUUID)
518
        fMaxUUID = uuid+1;
519
520
521
522

    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (client->GetClientControl()->fSessionID==uuid)) {
523
524
            client->GetClientControl()->fSessionID = GetNewUUID();
        }
525
526
527
    }
}

528
529
530
531
532
533
534
int JackEngine::GetClientPID(const char* name)
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
            return client->GetClientControl()->fPID;
    }
sletz's avatar
sletz committed
535

536
537
538
    return 0;
}

sletz's avatar
sletz committed
539
540
541
542
543
544
545
int JackEngine::GetClientRefNum(const char* name)
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
            return client->GetClientControl()->fRefNum;
    }
sletz's avatar
sletz committed
546

sletz's avatar
sletz committed
547
548
549
    return -1;
}

sletz's avatar
sletz committed
550
// Used for external clients
551
int JackEngine::ClientExternalOpen(const char* name, int pid, int uuid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
sletz's avatar
sletz committed
552
{
553
554
555
    char real_name[JACK_CLIENT_NAME_SIZE+1];

    if (uuid < 0) {
556
557
        uuid = GetNewUUID();
        strncpy( real_name, name, JACK_CLIENT_NAME_SIZE );
558
    } else {
559
560
        std::map<int,std::string>::iterator res = fReservationMap.find(uuid);
        if (res != fReservationMap.end()) {
561
            strncpy(real_name, res->second.c_str(), JACK_CLIENT_NAME_SIZE);
562
563
            fReservationMap.erase(uuid);
        } else {
564
            strncpy(real_name, name, JACK_CLIENT_NAME_SIZE);
565
566
567
        }

        EnsureUUID(uuid);
568
569
    }

570
    jack_log("JackEngine::ClientExternalOpen: uuid=%d, name = %s ", uuid, real_name);
sletz's avatar
sletz committed
571
572

    int refnum = AllocateRefnum();
sletz's avatar
sletz committed
573
574
575
576
    if (refnum < 0) {
        jack_error("No more refnum available");
        return -1;
    }
sletz's avatar
sletz committed
577
578

    JackExternalClient* client = new JackExternalClient();
sletz's avatar
sletz committed
579

580
    if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) {
581
        jack_error("Cannot allocate synchro");
sletz's avatar
sletz committed
582
        goto error;
sletz's avatar
sletz committed
583
584
    }

585
    if (client->Open(real_name, pid, refnum, uuid, shared_client) < 0) {
sletz's avatar
sletz committed
586
        jack_error("Cannot open client");
587
        goto error;
sletz's avatar
sletz committed
588
589
    }

590
    if (!fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
sletz's avatar
sletz committed
591
592
        // Failure if RT thread is not running (problem with the driver...)
        jack_error("Driver is not running");
593
        goto error;
sletz's avatar
sletz committed
594
595
    }

596
597
    fClientTable[refnum] = client;

598
    if (NotifyAddClient(client, real_name, refnum) < 0) {
sletz's avatar
sletz committed
599
        jack_error("Cannot notify add client");
600
        goto error;
sletz's avatar
sletz committed
601
    }
sletz's avatar
sletz committed
602

sletz's avatar
sletz committed
603
604
    fGraphManager->InitRefNum(refnum);
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
605
606
607
    *shared_engine = fEngineControl->GetShmIndex();
    *shared_graph_manager = fGraphManager->GetShmIndex();
    *ref = refnum;
608
609
610
    return 0;

error:
611
    // Cleanup...
612
    fSynchroTable[refnum].Destroy();
613
    fClientTable[refnum] = 0;
sletz's avatar
sletz committed
614
    client->Close();
sletz's avatar
sletz committed
615
    delete client;
sletz's avatar
sletz committed
616
617
618
619
    return -1;
}

// Used for server driver clients
620
int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait)
sletz's avatar
sletz committed
621
{
622
    jack_log("JackEngine::ClientInternalOpen: name = %s", name);
sletz's avatar
sletz committed
623
624
625

    int refnum = AllocateRefnum();
    if (refnum < 0) {
sletz's avatar
sletz committed
626
        jack_error("No more refnum available");
627
        goto error;
sletz's avatar
sletz committed
628
629
    }

630
    if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
sletz's avatar
sletz committed
631
        jack_error("Cannot allocate synchro");
632
        goto error;
sletz's avatar
sletz committed
633
    }
sletz's avatar
sletz committed
634

635
    if (wait && !fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
636
637
        // Failure if RT thread is not running (problem with the driver...)
        jack_error("Driver is not running");
638
        goto error;
639
    }
sletz's avatar
sletz committed
640

641
642
    fClientTable[refnum] = client;

sletz's avatar
sletz committed
643
644
    if (NotifyAddClient(client, name, refnum) < 0) {
        jack_error("Cannot notify add client");
645
        goto error;
sletz's avatar
sletz committed
646
647
    }

sletz's avatar
sletz committed
648
649
    fGraphManager->InitRefNum(refnum);
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
650
651
652
653
    *shared_engine = fEngineControl;
    *shared_manager = fGraphManager;
    *ref = refnum;
    return 0;
654
655
656

error:
    // Cleanup...
657
    fSynchroTable[refnum].Destroy();
658
659
    fClientTable[refnum] = 0;
    return -1;
sletz's avatar
sletz committed
660
661
}

662
// Used for external clients
sletz's avatar
sletz committed
663
int JackEngine::ClientExternalClose(int refnum)
sletz's avatar
sletz committed
664
665
{
    JackClientInterface* client = fClientTable[refnum];
666
667
668
669
670
    fEngineControl->fTransport.ResetTimebase(refnum);
    int res = ClientCloseAux(refnum, client, true);
    client->Close();
    delete client;
    return res;
sletz's avatar
sletz committed
671
672
}

673
674
// Used for server internal clients or drivers when the RT thread is stopped
int JackEngine::ClientInternalClose(int refnum, bool wait)
sletz's avatar
sletz committed
675
676
{
    JackClientInterface* client = fClientTable[refnum];
677
    return ClientCloseAux(refnum, client, wait);
sletz's avatar
sletz committed
678
679
680
681
}

int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
{
682
    jack_log("JackEngine::ClientCloseAux ref = %ld", refnum);
sletz's avatar
sletz committed
683

684
    // Unregister all ports ==> notifications are sent
sletz's avatar
sletz committed
685
686
687
688
689
    jack_int_t ports[PORT_NUM_FOR_CLIENT];
    int i;

    fGraphManager->GetInputPorts(refnum, ports);
    for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
690
        PortUnRegister(refnum, ports[i]);
sletz's avatar
sletz committed
691
692
693
694
    }

    fGraphManager->GetOutputPorts(refnum, ports);
    for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
695
        PortUnRegister(refnum, ports[i]);
sletz's avatar
sletz committed
696
    }
sletz's avatar
sletz committed
697

698
699
    // Remove the client from the table
    ReleaseRefnum(refnum);
sletz's avatar
sletz committed
700
701

    // Remove all ports
sletz's avatar
Typo    
sletz committed
702
    fGraphManager->RemoveAllPorts(refnum);
sletz's avatar
sletz committed
703
704
705

    // Wait until next cycle to be sure client is not used anymore
    if (wait) {
706
        if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
sletz's avatar
sletz committed
707
            jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum);
sletz's avatar
sletz committed
708
        }
sletz's avatar
Typo    
sletz committed
709
    }
sletz's avatar
sletz committed
710

sletz's avatar
Typo    
sletz committed
711
    // Notify running clients
712
    NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
sletz's avatar
sletz committed
713
714

    // Cleanup...
715
    fSynchroTable[refnum].Destroy();
sletz's avatar
sletz committed
716
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
717
718
719
    return 0;
}

sletz's avatar
sletz committed
720
int JackEngine::ClientActivate(int refnum, bool is_real_time)
sletz's avatar
sletz committed
721
722
{
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
723
    jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
724

sletz's avatar
sletz committed
725
    if (is_real_time)
726
        fGraphManager->Activate(refnum);
sletz's avatar
sletz committed
727

sletz's avatar
sletz committed
728
    // Wait for graph state change to be effective
729
    if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
sletz's avatar
sletz committed
730
731
732
        jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
        return -1;
    } else {
733
734
735
736
        jack_int_t input_ports[PORT_NUM_FOR_CLIENT];
        jack_int_t output_ports[PORT_NUM_FOR_CLIENT];
        fGraphManager->GetInputPorts(refnum, input_ports);
        fGraphManager->GetOutputPorts(refnum, output_ports);
737

738
739
        // Notify client
        NotifyActivate(refnum);
740

741
        // Then issue port registration notification
742
        for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
743
744
            NotifyPortRegistation(input_ports[i], true);
        }
745
        for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
746
            NotifyPortRegistation(output_ports[i], true);
747
748
        }

sletz's avatar
sletz committed
749
750
        return 0;
    }
sletz's avatar
sletz committed
751
752
753
754
755
756
}

// May be called without client
int JackEngine::ClientDeactivate(int refnum)
{
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
757
    jack_log("JackEngine::ClientDeactivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
sletz's avatar
sletz committed
758

759
760
761
762
    jack_int_t input_ports[PORT_NUM_FOR_CLIENT];
    jack_int_t output_ports[PORT_NUM_FOR_CLIENT];
    fGraphManager->GetInputPorts(refnum, input_ports);
    fGraphManager->GetOutputPorts(refnum, output_ports);
sletz's avatar
sletz committed
763

764
765
766
    // First disconnect all ports and remove their JackPortIsActive state
    for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
        PortDisconnect(refnum, input_ports[i], ALL_PORTS);
767
    }
768
769
770
    for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
        PortDisconnect(refnum, output_ports[i], ALL_PORTS);
    }
771

772
773
774
775
776
777
    // Then issue port registration notification
    for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) {
        NotifyPortRegistation(input_ports[i], false);
    }
    for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) {
        NotifyPortRegistation(output_ports[i], false);
778
    }
sletz's avatar
sletz committed
779

sletz's avatar
sletz committed
780
781
782
783
    fGraphManager->Deactivate(refnum);
    fLastSwitchUsecs = 0; // Force switch to occur next cycle, even when called with "dead" clients

    // Wait for graph state change to be effective
784
    if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
sletz's avatar
sletz committed
785
786
787
788
789
        jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
        return -1;
    } else {
        return 0;
    }
sletz's avatar
sletz committed
790
791
792
793
794
795
}

//-----------------
// Port management
//-----------------

796
int JackEngine::PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
sletz's avatar
sletz committed
797
{
sletz's avatar
sletz committed
798
    jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum, name, type, flags, buffer_size);
799
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
800

801
    // Check if port name already exists
sletz's avatar
sletz committed
802
    if (fGraphManager->GetPort(name) != NO_PORT) {
803
        jack_error("port_name \"%s\" already exists", name);
sletz's avatar
sletz committed
804
        return -1;
805
    }
sletz's avatar
sletz committed
806

807
    *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize);
sletz's avatar
sletz committed
808
    if (*port_index != NO_PORT) {
809
810
        if (client->GetClientControl()->fActive)
            NotifyPortRegistation(*port_index, true);
sletz's avatar
sletz committed
811
812
813
814
815
816
817
818
        return 0;
    } else {
        return -1;
    }
}

int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
{
sletz's avatar
sletz committed
819
    jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum, port_index);
820
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
821

822
823
    // Disconnect port ==> notification is sent
    PortDisconnect(refnum, port_index, ALL_PORTS);
sletz's avatar
sletz committed
824

825
    if (fGraphManager->ReleasePort(refnum, port_index) == 0) {
826
827
        if (client->GetClientControl()->fActive)
            NotifyPortRegistation(port_index, false);
sletz's avatar
sletz committed
828
829
830
831
832
833
834
835
        return 0;
    } else {
        return -1;
    }
}

int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
{
sletz's avatar
sletz committed
836
    jack_log("JackEngine::PortConnect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
837
838
    jack_port_id_t port_src, port_dst;

839
    return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
sletz's avatar
sletz committed
840
841
842
843
844
845
           ? -1
           : PortConnect(refnum, port_src, port_dst);
}

int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
{
sletz's avatar
sletz committed
846
    jack_log("JackEngine::PortConnect src = %d dst = %d", src, dst);
sletz's avatar
sletz committed
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
    JackClientInterface* client;
    int ref;

    if (fGraphManager->CheckPorts(src, dst) < 0)
        return -1;

    ref = fGraphManager->GetOutputRefNum(src);
    assert(ref >= 0);
    client = fClientTable[ref];
    assert(client);
    if (!client->GetClientControl()->fActive) {
        jack_error("Cannot connect ports owned by inactive clients:"
                   " \"%s\" is not active", client->GetClientControl()->fName);
        return -1;
    }

    ref = fGraphManager->GetInputRefNum(dst);
    assert(ref >= 0);
    client = fClientTable[ref];
    assert(client);
    if (!client->GetClientControl()->fActive) {
        jack_error("Cannot connect ports owned by inactive clients:"
                   " \"%s\" is not active", client->GetClientControl()->fName);
        return -1;
    }
sletz's avatar
sletz committed
872
873

    int res = fGraphManager->Connect(src, dst);
sletz's avatar
sletz committed
874
    if (res == 0)
sletz's avatar
sletz committed
875
876
        NotifyPortConnect(src, dst, true);
    return res;
sletz's avatar
sletz committed
877
878
}

879
880
int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
{
sletz's avatar
sletz committed
881
    jack_log("JackEngine::PortDisconnect src = %s dst = %s", src, dst);
882
    jack_port_id_t port_src, port_dst;
sletz's avatar
sletz committed
883

884
885
886
    return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
           ? -1
           : PortDisconnect(refnum, port_src, port_dst);
887
888
}

sletz's avatar
sletz committed
889
890
int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
{
sletz's avatar
sletz committed
891
    jack_log("JackEngine::PortDisconnect src = %d dst = %d", src, dst);
892

sletz's avatar
sletz committed
893
    if (dst == ALL_PORTS) {
sletz's avatar
sletz committed
894

895
        jack_int_t connections[CONNECTION_NUM_FOR_PORT];
sletz's avatar
sletz committed
896
897
898
        fGraphManager->GetConnections(src, connections);

        JackPort* port = fGraphManager->GetPort(src);
899
        int ret = 0;
sletz's avatar
sletz committed
900
        if (port->GetFlags() & JackPortIsOutput) {
901
            for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
902
903
904
                if (PortDisconnect(refnum, src, connections[i]) != 0) {
                    ret = -1;
                }
sletz's avatar
sletz committed
905
906
            }
        } else {
907
            for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
908
909
910
                if (PortDisconnect(refnum, connections[i], src) != 0) {
                    ret = -1;
                }
sletz's avatar
sletz committed
911
912
913
            }
        }

914
        return ret;
sletz's avatar
sletz committed
915
916
917
918
919
920
    } else if (fGraphManager->CheckPorts(src, dst) < 0) {
        return -1;
    } else if (fGraphManager->Disconnect(src, dst) == 0) {
        // Notifications
        NotifyPortConnect(src, dst, false);
        return 0;
sletz's avatar
sletz committed
921
    } else {
sletz's avatar
sletz committed
922
923
        return -1;
    }
sletz's avatar
sletz committed
924
925
}

926
927
int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name)
{
928
929
    char old_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
    strcpy(old_name, fGraphManager->GetPort(port)->GetName());
sletz's avatar
sletz committed
930
    fGraphManager->GetPort(port)->SetName(name);
931
    NotifyPortRename(port, old_name);
sletz's avatar
sletz committed
932
    return 0;
933
}
sletz's avatar
sletz committed
934

935
936
937
938
//--------------------
// Session management
//--------------------

Stéphane Letz's avatar
Stéphane Letz committed
939
void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket)
940
{
Torben Hohn's avatar
Torben Hohn committed
941
    if (fSessionPendingReplies != 0) {
942
943
944
945
        JackSessionNotifyResult res(-1);
        res.Write(socket);
        jack_log("JackEngine::SessionNotify ... busy");
        return;
Torben Hohn's avatar
Torben Hohn committed
946
947
    }

948
949
950
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (client->GetClientControl()->fSessionID < 0)) {
951
952
            client->GetClientControl()->fSessionID = GetNewUUID();
        }
953
    }
Torben Hohn's avatar
Torben Hohn committed
954
955
956
957
958
959
    fSessionResult = new JackSessionNotifyResult();

    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && client->GetClientControl()->fCallback[kSessionCallback]) {

960
961
962
963
964
965
            // check if this is a notification to a specific client.
            if (target!=NULL && strlen(target)!=0) {
                if (strcmp(target, client->GetClientControl()->fName)) {
                    continue;
                }
            }
Torben Hohn's avatar
Torben Hohn committed
966

967
968
            char path_buf[JACK_PORT_NAME_SIZE];
            snprintf( path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR );
969

970
            int res = JackTools::MkDir(path_buf);
971
            if (res)
972
                jack_error( "JackEngine::SessionNotify: can not create session directory '%s'", path_buf );