jack2/windows/JackWinNamedPipeServerChann...

269 lines
7.3 KiB
C++

/*
Copyright (C) 2004-2008 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "JackWinNamedPipeServerChannel.h"
#include "JackNotification.h"
#include "JackRequest.h"
#include "JackServer.h"
#include "JackLockedEngine.h"
#include "JackGlobals.h"
#include "JackClient.h"
#include "JackNotification.h"
#include "JackException.h"
#include <assert.h>
using namespace std;
namespace Jack
{
HANDLE JackClientPipeThread::fMutex = NULL; // Never released....
// fRefNum = -1 correspond to already removed client
JackClientPipeThread::JackClientPipeThread(JackWinNamedPipeClient* pipe)
:fPipe(pipe), fDecoder(NULL), fServer(NULL), fThread(this), fRefNum(0)
{
// First one allocated the static fMutex
if (fMutex == NULL) {
fMutex = CreateMutex(NULL, FALSE, NULL);
}
}
JackClientPipeThread::~JackClientPipeThread()
{
jack_log("JackClientPipeThread::~JackClientPipeThread");
delete fPipe;
}
int JackClientPipeThread::Open(JackServer* server) // Open the Server/Client connection
{
// Start listening
if (fThread.Start() != 0) {
jack_error("Cannot start Jack server listener\n");
return -1;
} else {
fDecoder = new JackRequestDecoder(server, this);
fServer = server;
return 0;
}
}
void JackClientPipeThread::Close() // Close the Server/Client connection
{
jack_log("JackClientPipeThread::Close 0 %x %ld", this, fRefNum);
//fThread.Kill();
fPipe->Close();
fRefNum = -1;
delete fDecoder;
fDecoder = NULL;
}
bool JackClientPipeThread::Execute()
{
try {
jack_log("JackClientPipeThread::Execute %x", this);
JackRequest header;
int res = header.Read(fPipe);
bool ret = true;
// Lock the global mutex
if (WaitForSingleObject(fMutex, INFINITE) == WAIT_FAILED) {
jack_error("JackClientPipeThread::Execute : mutex wait error");
}
// Decode header
if (res < 0) {
jack_log("JackClientPipeThread::Execute : cannot decode header");
ClientKill();
ret = false;
// Decode request
} else if (fDecoder->HandleRequest(fPipe, header.fType) < 0) {
ret = false;
}
// Unlock the global mutex
if (!ReleaseMutex(fMutex)) {
jack_error("JackClientPipeThread::Execute : mutex release error");
}
return ret;
} catch (JackQuitException& e) {
jack_log("JackClientPipeThread::Execute : JackQuitException");
return false;
}
}
void JackClientPipeThread::ClientAdd(detail::JackChannelTransactionInterface* socket, JackClientOpenRequest* req, JackClientOpenResult *res)
{
jack_log("JackClientPipeThread::ClientAdd %x %s", this, req->fName);
fRefNum = -1;
res->fResult = fServer->GetEngine()->ClientExternalOpen(req->fName, req->fPID, req->fUUID, &fRefNum, &res->fSharedEngine, &res->fSharedClient, &res->fSharedGraph);
}
void JackClientPipeThread::ClientRemove(detail::JackChannelTransactionInterface* socket_aux, int refnum)
{
jack_log("JackClientPipeThread::ClientRemove ref = %d", refnum);
Close();
}
void JackClientPipeThread::ClientKill()
{
jack_log("JackClientPipeThread::ClientKill ref = %d", fRefNum);
if (fRefNum == -1) { // Correspond to an already removed client.
jack_log("Kill a closed client %x", this);
} else if (fRefNum == 0) { // Correspond to a still not opened client.
jack_log("Kill a not opened client %x", this);
} else {
fServer->GetEngine()->ClientKill(fRefNum);
}
Close();
}
JackWinNamedPipeServerChannel::JackWinNamedPipeServerChannel():fThread(this)
{}
JackWinNamedPipeServerChannel::~JackWinNamedPipeServerChannel()
{
std::list<JackClientPipeThread*>::iterator it;
for (it = fClientList.begin(); it != fClientList.end(); it++) {
JackClientPipeThread* client = *it;
client->Close();
delete client;
}
}
int JackWinNamedPipeServerChannel::Open(const char* server_name, JackServer* server)
{
jack_log("JackWinNamedPipeServerChannel::Open");
snprintf(fServerName, sizeof(fServerName), server_name);
// Needed for internal connection from JackWinNamedPipeServerNotifyChannel object
if (ClientListen()) {
fServer = server;
return 0;
} else {
jack_error("JackWinNamedPipeServerChannel::Open : cannot create result listen pipe");
return -1;
}
}
void JackWinNamedPipeServerChannel::Close()
{
/* TODO : solve WIN32 thread Kill issue
This would hang the server... since we are quitting it, its not really problematic,
all resources will be deallocated at the end.
fRequestListenPipe.Close();
fThread.Stop();
*/
fRequestListenPipe.Close();
}
int JackWinNamedPipeServerChannel::Start()
{
if (fThread.Start() != 0) {
jack_error("Cannot start Jack server listener");
return -1;
} else {
return 0;
}
}
void JackWinNamedPipeServerChannel::Stop()
{
fThread.Kill();
}
bool JackWinNamedPipeServerChannel::Init()
{
jack_log("JackWinNamedPipeServerChannel::Init");
// Accept first client, that is the JackWinNamedPipeServerNotifyChannel object
return ClientAccept();
}
bool JackWinNamedPipeServerChannel::ClientListen()
{
if (fRequestListenPipe.Bind(jack_server_dir, fServerName, 0) < 0) {
jack_error("JackWinNamedPipeServerChannel::ClientListen : cannot create result listen pipe");
return false;
} else {
return true;
}
}
bool JackWinNamedPipeServerChannel::ClientAccept()
{
JackWinNamedPipeClient* pipe;
if ((pipe = fRequestListenPipe.AcceptClient()) == NULL) {
jack_error("JackWinNamedPipeServerChannel::ClientAccept : cannot connect pipe");
return false;
} else {
ClientAdd(pipe);
return true;
}
}
bool JackWinNamedPipeServerChannel::Execute()
{
if (!ClientListen()) {
return false;
}
return ClientAccept();
}
void JackWinNamedPipeServerChannel::ClientAdd(JackWinNamedPipeClient* pipe)
{
// Remove dead (= not running anymore) clients.
std::list<JackClientPipeThread*>::iterator it = fClientList.begin();
JackClientPipeThread* client;
jack_log("JackWinNamedPipeServerChannel::ClientAdd size %ld", fClientList.size());
while (it != fClientList.end()) {
client = *it;
if (client->IsRunning()) {
it++;
} else {
it = fClientList.erase(it);
delete client;
}
}
client = new JackClientPipeThread(pipe);
client->Open(fServer);
// Here we are sure that the client is running (because it's thread is in "running" state).
fClientList.push_back(client);
}
} // end of namespace