IPC: original socket ipc implementation

This commit is contained in:
Gauvain 'GovanifY' Roussel-Tarbouriech 2020-08-08 08:43:41 +02:00 committed by tellowkrinkle
parent c422aa00aa
commit 86757fd36f
13 changed files with 422 additions and 2 deletions

View File

@ -75,6 +75,7 @@ set(pcsx2Sources
IopIrq.cpp
IopMem.cpp
IopSio2.cpp
IPC.cpp
Mdec.cpp
Memory.cpp
MMI.cpp
@ -147,6 +148,7 @@ set(pcsx2Headers
IopHw.h
IopMem.h
IopSio2.h
IPC.h
Mdec.h
MTVU.h
Memory.h

View File

@ -454,6 +454,7 @@ struct Pcsx2Config
CdvdShareWrite :1, // allows the iso to be modified while it's loaded
EnablePatches :1, // enables patch detection and application
EnableCheats :1, // enables cheat detection and application
EnableIPC :1, // enables inter-process communication
EnableWideScreenPatches :1,
#ifndef DISABLE_RECORDING
EnableRecordingTools :1,

256
pcsx2/IPC.cpp Normal file
View File

@ -0,0 +1,256 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <sys/types.h>
#if _WIN32
#define read_portable(a, b, c) (recv(a, b, c, 0))
#define write_portable(a, b, c) (send(a, b, c, 0))
#define bzero(b, len) (memset((b), '\0', (len)), (void)0)
#include <windows.h>
#else
#define read_portable(a, b, c) (read(a, b, c))
#define write_portable(a, b, c) (write(a, b, c))
#include <sys/socket.h>
#include <sys/un.h>
#endif
#include "Common.h"
#include "Memory.h"
#include "System/SysThreads.h"
#include "IPC.h"
SocketIPC::SocketIPC(SysCoreThread* vm)
: pxThread("IPC_Socket")
{
#ifdef _WIN32
WSADATA wsa;
SOCKET new_socket;
struct sockaddr_in server, client;
int c;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
Console.WriteLn(Color_Red, "IPC: Cannot initialize winsock! Shutting down...");
return;
}
if ((m_sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
Console.WriteLn(Color_Red, "IPC: Cannot open socket! Shutting down...");
return;
}
// yes very good windows s/sun/sin/g sure is fine
server.sin_family = AF_INET;
// localhost only
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(PORT);
if (bind(m_sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
{
Console.WriteLn(Color_Red, "IPC: Error while binding to socket! Shutting down...");
return;
}
#else
struct sockaddr_un server;
m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (m_sock < 0)
{
Console.WriteLn(Color_Red, "IPC: Cannot open socket! Shutting down...");
return;
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, SOCKET_NAME);
// we unlink the socket so that when releasing this thread the socket gets
// freed even if we didn't close correctly the loop
unlink(SOCKET_NAME);
if (bind(m_sock, (struct sockaddr*)&server, sizeof(struct sockaddr_un)))
{
Console.WriteLn(Color_Red, "IPC: Error while binding to socket! Shutting down...");
return;
}
#endif
// maximum queue of SOMAXCONN commands before refusing, which stops the thread
listen(m_sock, SOMAXCONN);
// we save a handle of the main vm object
m_vm = vm;
// we start the thread
Start();
}
void SocketIPC::ExecuteTaskInThread()
{
int msgsock = 0;
// for the sake of speed we malloc once a return buffer and reuse it by just
// cropping its size when needed, it is 450k long which is the size of 50k
// MsgWrite64 replies, should be good enough even if we implement batch IPC
// processing. Coincidentally 650k is the size of 50k MsgWrite64 REQUESTS so
// we just allocate a 1mb buffer in the end, lul
ret_buffer = (char*)malloc(450000 * sizeof(char));
ipc_buffer = (char*)malloc(650000 * sizeof(char));
while (true)
{
msgsock = accept(m_sock, 0, 0);
if (msgsock == -1)
{
return;
}
else
{
if (read_portable(msgsock, ipc_buffer, 650000) < 0)
{
return;
}
else
{
auto res = ParseCommand(ipc_buffer, ret_buffer);
if (write_portable(msgsock, res.second, res.first) < 0)
{
return;
}
}
}
}
}
SocketIPC::~SocketIPC()
{
#ifdef _WIN32
closesocket(m_sock);
WSACleanup();
#else
close(m_sock);
unlink(SOCKET_NAME);
#endif
free(ret_buffer);
free(ipc_buffer);
// destroy the thread
try
{
pxThread::Cancel();
}
DESTRUCTOR_CATCHALL
}
char* SocketIPC::MakeOkIPC(char* ret_buffer)
{
ret_buffer[0] = (unsigned char)IPC_OK;
return ret_buffer;
}
char* SocketIPC::MakeFailIPC(char* ret_buffer)
{
ret_buffer[0] = (unsigned char)IPC_FAIL;
return ret_buffer;
}
std::pair<int, char*> SocketIPC::ParseCommand(char* buf, char* ret_buffer)
{
// currently all our instructions require a running VM so we check once
// here, will help perf when/if we implement multi-ipc processing in one
// socket roundtrip.
if (!m_vm->HasActiveMachine())
return std::make_pair(1, MakeFailIPC(ret_buffer));
// IPC Message event (1 byte)
// | Memory address (4 byte)
// | | argument (VLE)
// | | |
// format: XX YY YY YY YY ZZ ZZ ZZ ZZ
// reply code: 00 = OK, FF = NOT OK
// | return value (VLE)
// | |
// reply: XX ZZ ZZ ZZ ZZ
IPCCommand opcode = (IPCCommand)buf[0];
// return value
std::pair<int, char*> rval;
// YY YY YY YY from schema above
u32 a = FromArray<u32>(buf, 1);
switch (opcode)
{
case MsgRead8:
{
u8 res;
res = memRead8(a);
rval = std::make_pair(2, ToArray(MakeOkIPC(ret_buffer), res, 1));
break;
}
case MsgRead16:
{
u16 res;
res = memRead16(a);
rval = std::make_pair(3, ToArray(MakeOkIPC(ret_buffer), res, 1));
break;
}
case MsgRead32:
{
u32 res;
res = memRead32(a);
rval = std::make_pair(5, ToArray(MakeOkIPC(ret_buffer), res, 1));
break;
}
case MsgRead64:
{
u64 res;
memRead64(a, &res);
rval = std::make_pair(9, ToArray(MakeOkIPC(ret_buffer), res, 1));
break;
}
case MsgWrite8:
{
memWrite8(a, FromArray<u8>(buf, 5));
rval = std::make_pair(1, MakeOkIPC(ret_buffer));
break;
}
case MsgWrite16:
{
memWrite16(a, FromArray<u16>(buf, 5));
rval = std::make_pair(1, MakeOkIPC(ret_buffer));
break;
}
case MsgWrite32:
{
memWrite32(a, FromArray<u32>(buf, 5));
rval = std::make_pair(1, MakeOkIPC(ret_buffer));
break;
}
case MsgWrite64:
{
memWrite64(a, FromArray<u64>(buf, 5));
rval = std::make_pair(1, MakeOkIPC(ret_buffer));
break;
}
default:
{
rval = std::make_pair(1, MakeFailIPC(ret_buffer));
break;
}
}
return rval;
}

122
pcsx2/IPC.h Normal file
View File

@ -0,0 +1,122 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
/* Client code example for interfacing with the IPC interface is available
* here: https://code.govanify.com/govanify/pcsx2_ipc/ */
#pragma once
#include "Utilities/PersistentThread.h"
#include "System/SysThreads.h"
using namespace Threading;
class SocketIPC : public pxThread
{
typedef pxThread _parent;
protected:
#ifdef _WIN32
// windows claim to have support for AF_UNIX sockets but that is a blatant lie,
// their SDK won't even run their own examples, so we go on TCP sockets.
#define PORT 28011
#else
// absolute path of the socket. Stored in the temporary directory in linux since
// /run requires superuser permission
const char* SOCKET_NAME = "/tmp/pcsx2.sock";
#endif
// socket handlers
#ifdef _WIN32
SOCKET m_sock = INVALID_SOCKET;
#else
int m_sock = 0;
#endif
// buffers that store the ipc request and reply messages.
char* ret_buffer;
char* ipc_buffer;
// possible command messages
enum IPCCommand
{
MsgRead8 = 0,
MsgRead16 = 1,
MsgRead32 = 2,
MsgRead64 = 3,
MsgWrite8 = 4,
MsgWrite16 = 5,
MsgWrite32 = 6,
MsgWrite64 = 7
};
// possible result codes
enum IPCResult
{
IPC_OK = 0,
IPC_FAIL = 0xFF
};
// handle to the main vm thread
SysCoreThread* m_vm;
/* Thread used to relay IPC commands. */
void ExecuteTaskInThread();
/* Internal function, Parses an IPC command.
* buf: buffer containing the IPC command.
* ret_buffer: buffer that will be used to send the reply.
* return value: pair containing a buffer with the result
* of the command and its size. */
std::pair<int, char*> ParseCommand(char* buf, char* ret_buffer);
/* Formats an IPC buffer
* ret_buffer: return buffer to use.
* return value: buffer containing the status code allocated of size
* size */
static inline char* MakeOkIPC(char* ret_buffer);
static inline char* MakeFailIPC(char* ret_buffer);
/* Converts an uint to an char* in little endian
* res_array: the array to modify
* res: the value to convert
* i: when to insert it into the array
* return value: res_array
* NB: implicitely inlined */
template <typename T>
static char* ToArray(char* res_array, T res, int i)
{
memcpy((res_array + i), (char*)&res, sizeof(T));
return res_array;
}
/* Converts a char* to an uint in little endian
* arr: the array to convert
* i: when to load it from the array
* return value: the converted value
* NB: implicitely inlined */
template <typename T>
static T FromArray(char* arr, int i)
{
return *(T*)(arr + i);
}
public:
/* Initializers */
SocketIPC(SysCoreThread* vm);
virtual ~SocketIPC();
}; // class SocketIPC

View File

@ -436,6 +436,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
IniBitBool( CdvdShareWrite );
IniBitBool( EnablePatches );
IniBitBool( EnableCheats );
IniBitBool( EnableIPC );
IniBitBool( EnableWideScreenPatches );
#ifndef DISABLE_RECORDING
IniBitBool( EnableRecordingTools );

View File

@ -24,6 +24,7 @@
#include "Patch.h"
#include "SysThreads.h"
#include "MTVU.h"
#include "IPC.h"
#include "../DebugTools/MIPSAnalyst.h"
#include "../DebugTools/SymbolMap.h"
@ -243,6 +244,11 @@ void SysCoreThread::GameStartingInThread()
#ifdef USE_SAVESLOT_UI_UPDATES
UI_UpdateSysControls();
#endif
if(EmuConfig.EnableIPC && m_IpcState == OFF)
{
m_IpcState = ON;
m_socketIpc = std::make_unique<SocketIPC>(this);
}
}
bool SysCoreThread::StateCheckInThread()

View File

@ -19,6 +19,7 @@
#include "Utilities/PersistentThread.h"
#include "x86emitter/tools.h"
#include "IPC.h"
using namespace Threading;
@ -170,6 +171,17 @@ protected:
bool m_resetVsyncTimers;
bool m_resetVirtualMachine;
// Stores the state of the socket IPC thread.
std::unique_ptr<SocketIPC> m_socketIpc;
// Current state of the IPC thread
enum StateIPC
{
OFF,
ON
};
StateIPC m_IpcState = OFF;
// Indicates if the system has an active virtual machine state. Pretty much always
// true anytime between plugins being initialized and plugins being shutdown. Gets
// set false when plugins are shutdown, the corethread is canceled, or when an error

View File

@ -123,6 +123,7 @@ enum MenuIdentifiers
MenuId_GameSettingsSubMenu,
MenuId_EnablePatches,
MenuId_EnableCheats,
MenuId_EnableIPC,
MenuId_EnableWideScreenPatches,
MenuId_EnableInputRecording,
MenuId_EnableLuaTools,

View File

@ -216,6 +216,7 @@ void MainEmuFrame::ConnectMenus()
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnablePatches_Click, this, MenuId_EnablePatches);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableCheats_Click, this, MenuId_EnableCheats);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableIPC_Click, this, MenuId_EnableIPC);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableWideScreenPatches_Click, this, MenuId_EnableWideScreenPatches);
#ifndef DISABLE_RECORDING
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableRecordingTools_Click, this, MenuId_EnableInputRecording);
@ -368,6 +369,9 @@ void MainEmuFrame::CreatePcsx2Menu()
m_GameSettingsSubmenu.Append(MenuId_EnableCheats, _("Enable &Cheats"),
wxEmptyString, wxITEM_CHECK);
m_GameSettingsSubmenu.Append(MenuId_EnableIPC, _("Enable &IPC"),
wxEmptyString, wxITEM_CHECK);
m_GameSettingsSubmenu.Append(MenuId_EnableWideScreenPatches, _("Enable &Widescreen Patches"),
_("Enabling Widescreen Patches may occasionally cause issues."), wxITEM_CHECK);
@ -744,6 +748,7 @@ void MainEmuFrame::ApplyConfigToGui(AppConfig& configToApply, int flags)
{//these should not be affected by presets
menubar.Check( MenuId_EnableBackupStates, configToApply.EmuOptions.BackupSavestate );
menubar.Check( MenuId_EnableCheats, configToApply.EmuOptions.EnableCheats );
menubar.Check( MenuId_EnableIPC, configToApply.EmuOptions.EnableIPC );
menubar.Check( MenuId_EnableWideScreenPatches, configToApply.EmuOptions.EnableWideScreenPatches );
#ifndef DISABLE_RECORDING
menubar.Check( MenuId_EnableInputRecording, configToApply.EmuOptions.EnableRecordingTools);

View File

@ -193,6 +193,7 @@ protected:
void Menu_EnableBackupStates_Click(wxCommandEvent &event);
void Menu_EnablePatches_Click(wxCommandEvent &event);
void Menu_EnableCheats_Click(wxCommandEvent &event);
void Menu_EnableIPC_Click(wxCommandEvent &event);
void Menu_EnableWideScreenPatches_Click(wxCommandEvent &event);
#ifndef DISABLE_RECORDING
void Menu_EnableRecordingTools_Click(wxCommandEvent &event);

View File

@ -528,6 +528,13 @@ void MainEmuFrame::Menu_EnableCheats_Click( wxCommandEvent& )
AppSaveSettings();
}
void MainEmuFrame::Menu_EnableIPC_Click( wxCommandEvent& )
{
g_Conf->EmuOptions.EnableIPC = GetMenuBar()->IsChecked( MenuId_EnableIPC );
AppApplySettings();
AppSaveSettings();
}
void MainEmuFrame::Menu_EnableWideScreenPatches_Click( wxCommandEvent& )
{
g_Conf->EmuOptions.EnableWideScreenPatches = GetMenuBar()->IsChecked( MenuId_EnableWideScreenPatches );

View File

@ -188,6 +188,7 @@
<ClCompile Include="..\..\gui\DriveList.cpp" />
<ClCompile Include="..\..\gui\Panels\MemoryCardListView.cpp" />
<ClCompile Include="..\..\IopGte.cpp" />
<ClCompile Include="..\..\IPC.cpp" />
<ClCompile Include="..\..\IPU\IPUdma.cpp" />
<ClCompile Include="..\..\IPU\IPUdither.cpp" />
<ClCompile Include="..\..\Linux\LnxConsolePipe.cpp">
@ -437,6 +438,7 @@
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h" />
<ClInclude Include="..\..\gui\Panels\MemoryCardPanels.h" />
<ClInclude Include="..\..\IopGte.h" />
<ClInclude Include="..\..\IPC.h" />
<ClInclude Include="..\..\IPU\IPUdma.h" />
<ClInclude Include="..\..\Mdec.h" />
<ClInclude Include="..\..\Patch.h" />
@ -624,4 +626,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -888,6 +888,8 @@
</ClCompile>
<ClCompile Include="..\..\gui\DriveList.cpp">
<Filter>AppHost</Filter>
<ClCompile Include="..\..\IPC.cpp">
<Filter>System</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
@ -1343,6 +1345,8 @@
</ClInclude>
<ClInclude Include="..\..\gui\DriveList.h">
<Filter>AppHost\Include</Filter>
<ClInclude Include="..\..\IPC.h">
<Filter>System\Include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
@ -1428,4 +1432,4 @@
<Filter>AppHost\Resources</Filter>
</Manifest>
</ItemGroup>
</Project>
</Project>