mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-15 23:40:29 +02:00
The core and the headless tools no longer use mutexes, condition variables, spinlocks, std::atomic, std::thread or volatile flags. Threads hand off through libretro-common's retro_atomic and rthreads plus a few small pieces on top of them in Common/Thread: Thread, MpscQueue, EventCounter, ParkingLot waits and ReaderGate. Every wait blocks until it's woken; nothing polls on a timer. State that only one thread touches just loses its lock. The PSP filesystem, sceIo and the savedata dialog now belong to the emulation thread: async reads and writes run when they're queued and complete later in emulated time, and the IO and savedata threads are gone. The friend finder only talks to the adhoc server and hands what it gets to the emulation thread. GPU init always runs on the thread that starts the game. The Vulkan render and compile queues, pipeline and render pass caches, delete list and SPIR-V cache, the GL render queue, the software renderer's jit caches, the file loader caches, the web debugger, UPnP, input, savestates, logging and the GE debugger all hand off through queues and atomics. The IR jit's spinning profiler thread goes. A CPU thread with nothing to run now sleeps until work is queued instead of spinning. Headless with --debugger waits for a client while stopped, and ends the run once every client that connected has left. Web server shutdown keeps servicing the CPU queue until the server has finished.
152 lines
3.8 KiB
C++
152 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <retro_atomic.h>
|
|
|
|
#include "Common/Net/HTTPHeaders.h"
|
|
#include "Common/Net/Resolve.h"
|
|
#include "Common/Thread/Thread.h"
|
|
|
|
// One thread per request. Run() and the destructor belong to one thread (the accept loop's);
|
|
// the destructor joins every worker, so handlers must finish on their own or be woken first.
|
|
class NewThreadExecutor {
|
|
public:
|
|
~NewThreadExecutor();
|
|
void Run(std::function<void()> func);
|
|
|
|
private:
|
|
// Joins the workers that have finished.
|
|
void Prune();
|
|
|
|
struct Worker {
|
|
Thread thread;
|
|
// The worker's last act.
|
|
retro_atomic_int_t done{ 0 };
|
|
};
|
|
std::vector<std::unique_ptr<Worker>> workers_;
|
|
};
|
|
|
|
namespace net {
|
|
|
|
class CancelToken;
|
|
class InputSink;
|
|
class OutputSink;
|
|
|
|
} // namespace net
|
|
|
|
namespace http {
|
|
|
|
class ServerRequest {
|
|
public:
|
|
ServerRequest(int fd);
|
|
~ServerRequest();
|
|
|
|
std::string_view resource() const {
|
|
return header_.resource;
|
|
}
|
|
|
|
RequestHeader::Method Method() const {
|
|
return header_.method;
|
|
}
|
|
|
|
const RequestHeader &Header() const {
|
|
return header_;
|
|
}
|
|
|
|
bool GetParamValue(const char *param_name, std::string *value) const {
|
|
return header_.GetParamValue(param_name, value);
|
|
}
|
|
// Use lowercase.
|
|
bool GetHeader(const char *name, std::string *value) const {
|
|
return header_.GetOther(name, value);
|
|
}
|
|
|
|
net::InputSink *In() const { return in_; }
|
|
net::OutputSink *Out() const { return out_; }
|
|
|
|
// TODO: Remove, in favor of PartialWrite and friends.
|
|
int fd() const { return fd_; }
|
|
|
|
void WritePartial() const;
|
|
void Write();
|
|
void Close();
|
|
|
|
bool IsOK() const { return fd_ > 0; }
|
|
|
|
// If size is negative, no Content-Length: line is written.
|
|
void WriteHttpResponseHeader(const char *ver, int status, int64_t size = -1, const char *mimeType = nullptr, const char *otherHeaders = nullptr) const;
|
|
|
|
private:
|
|
net::InputSink *in_;
|
|
net::OutputSink *out_;
|
|
RequestHeader header_;
|
|
int fd_;
|
|
};
|
|
|
|
// Register handlers on this class to serve stuff.
|
|
class Server {
|
|
public:
|
|
// Takes ownership.
|
|
Server(NewThreadExecutor *executor);
|
|
virtual ~Server();
|
|
|
|
typedef std::function<void(const ServerRequest &)> UrlHandlerFunc;
|
|
typedef std::map<std::string, UrlHandlerFunc, std::less<>> UrlHandlerMap;
|
|
|
|
// Runs forever, serving request. If you want to do something else than serve pages,
|
|
// better put this on a thread. Returns false if failed to start serving, never
|
|
// returns if successful.
|
|
bool Run(int port);
|
|
// Blocks until a connection arrives and hands it to the executor, or until cancel is set.
|
|
// Returns false when cancelled or on error.
|
|
bool RunSlice(const net::CancelToken *cancel);
|
|
bool Listen(int port, const char *reason, net::DNSType type = net::DNSType::ANY);
|
|
void Stop();
|
|
|
|
void RegisterHandler(const char *url_path, UrlHandlerFunc handler);
|
|
void SetFallbackHandler(UrlHandlerFunc handler);
|
|
|
|
// If you want to customize things at a lower level than just a simple path handler,
|
|
// then inherit and override this. Implementations should forward to HandleRequestDefault
|
|
// if they don't recognize the url.
|
|
virtual void HandleRequest(const ServerRequest &request);
|
|
|
|
int ListenerSocket() const {
|
|
return listenerSock_;
|
|
}
|
|
int Port() const {
|
|
return port_;
|
|
}
|
|
const std::string &LocalAddress() const {
|
|
return localAddress_;
|
|
}
|
|
|
|
private:
|
|
bool Listen6(int port, bool ipv6_only, const char *reason);
|
|
bool Listen4(int port, const char *reason);
|
|
|
|
void HandleConnection(int conn_fd);
|
|
|
|
// Things like default 404, etc.
|
|
void HandleRequestDefault(const ServerRequest &request);
|
|
|
|
// Neat built-in handlers that are tied to the server.
|
|
void HandleListing(const ServerRequest &request);
|
|
void Handle404(const ServerRequest &request);
|
|
|
|
int listenerSock_;
|
|
int port_ = 0;
|
|
std::string localAddress_;
|
|
|
|
UrlHandlerMap handlers_;
|
|
UrlHandlerFunc fallback_;
|
|
|
|
NewThreadExecutor *executor_;
|
|
};
|
|
|
|
} // namespace http
|