diff --git a/libctru/include/3ds/srv.h b/libctru/include/3ds/srv.h index 47aa4bc..d575697 100644 --- a/libctru/include/3ds/srv.h +++ b/libctru/include/3ds/srv.h @@ -10,6 +10,14 @@ Result srvInit(void); /// Exits the service API. void srvExit(void); +/** + * @brief Makes srvGetServiceHandle non-blocking for the current thread (or blocking, the default), in case of unavailable (full) requested services. + * @param blocking Whether srvGetServiceHandle should be non-blocking. + * srvGetServiceHandle will always block if the service hasn't been registered yet, + * use srvIsServiceRegistered to check whether that is the case or not. + */ +void srvSetBlockingPolicy(bool nonBlocking); + /** * @brief Gets the current service API session handle. * @return The current service API session handle. @@ -20,6 +28,9 @@ Handle *srvGetSessionHandle(void); * @brief Retrieves a service handle, retrieving from the environment handle list if possible. * @param out Pointer to write the handle to. * @param name Name of the service. + * @return 0 if no error occured, + * 0xD8E06406 if the caller has no right to access the service, + * 0xD0401834 if the requested service port is full and srvGetServiceHandle is non-blocking (see @ref srvSetBlockingPolicy). */ Result srvGetServiceHandle(Handle* out, const char* name); @@ -50,6 +61,9 @@ Result srvUnregisterService(const char* name); * @brief Retrieves a service handle. * @param out Pointer to output the handle to. * @param name Name of the service. + * * @return 0 if no error occured, + * 0xD8E06406 if the caller has no right to access the service, + * 0xD0401834 if the requested service port is full and srvGetServiceHandle is non-blocking (see @ref srvSetBlockingPolicy). */ Result srvGetServiceHandleDirect(Handle* out, const char* name); @@ -73,6 +87,12 @@ Result srvUnregisterPort(const char* name); */ Result srvGetPort(Handle* out, const char* name); +/** + * @brief Waits for a port to be registered. + * @param name Name of the port to wait for registration. + */ +Result srvWaitForPortRegistered(const char* name); + /** * @brief Subscribes to a notification. * @param notificationId ID of the notification. @@ -112,3 +132,10 @@ Result srvPublishAndGetSubscriber(u32* processIdCountOut, u32* processIdsOut, u3 * @param name Name of the service to check. */ Result srvIsServiceRegistered(bool* registeredOut, const char* name); + +/** + * @brief Checks whether a port is registered. + * @param registeredOut Pointer to output the registration status to. + * @param name Name of the port to check. + */ +Result srvIsPortRegistered(bool* registeredOut, const char* name); diff --git a/libctru/source/internal.h b/libctru/source/internal.h index 09316d1..e4d47cb 100644 --- a/libctru/source/internal.h +++ b/libctru/source/internal.h @@ -26,6 +26,9 @@ typedef struct // FS session override u32 fs_magic; Handle fs_session; + + // Whether srvGetServiceHandle is non-blocking in case of full service ports. + bool srv_blocking_policy; } ThreadVars; static inline ThreadVars* getThreadVars(void) diff --git a/libctru/source/srv.c b/libctru/source/srv.c index bc0bc35..9336db6 100644 --- a/libctru/source/srv.c +++ b/libctru/source/srv.c @@ -14,9 +14,17 @@ #include <3ds/os.h> #include <3ds/services/srvpm.h> +#include "internal.h" + static Handle srvHandle; static int srvRefCount; +static bool srvGetBlockingPolicy(void) +{ + ThreadVars *tv = getThreadVars(); + return tv->magic == THREADVARS_MAGIC && tv->srv_blocking_policy; +} + Result srvInit(void) { Result rc = 0; @@ -43,6 +51,12 @@ void srvExit(void) srvHandle = 0; } +void srvSetBlockingPolicy(bool nonBlocking) +{ + ThreadVars *tv = getThreadVars(); + tv->srv_blocking_policy = nonBlocking; +} + Handle *srvGetSessionHandle(void) { return &srvHandle; @@ -128,7 +142,7 @@ Result srvGetServiceHandleDirect(Handle* out, const char* name) cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100 strncpy((char*) &cmdbuf[1], name,8); cmdbuf[3] = strnlen(name, 8); - cmdbuf[4] = 0x0; + cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc; @@ -184,6 +198,20 @@ Result srvGetPort(Handle* out, const char* name) return cmdbuf[1]; } +Result srvWaitForPortRegistered(const char* name) +{ + Result rc = 0; + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x8,4,0); // 0x80100 + strncpy((char*) &cmdbuf[1], name,8); + cmdbuf[3] = strnlen(name, 8); + cmdbuf[4] = 0x1; + + if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc; + return cmdbuf[1]; +} + Result srvSubscribe(u32 notificationId) { Result rc = 0; @@ -269,3 +297,21 @@ Result srvIsServiceRegistered(bool* registeredOut, const char* name) return cmdbuf[1]; } + +Result srvIsPortRegistered(bool* registeredOut, const char* name) +{ + Handle port; + Result rc = srvGetPort(&port, name); + + if(rc == 0xD8801BFA) + { + if(registeredOut) *registeredOut = false; + return 0; + } + else if(R_SUCCEEDED(rc)) + { + if(registeredOut) *registeredOut = true; + svcCloseHandle(port); + } + return rc; +} diff --git a/libctru/source/system/syscalls.c b/libctru/source/system/syscalls.c index 711f79a..a0c2903 100644 --- a/libctru/source/system/syscalls.c +++ b/libctru/source/system/syscalls.c @@ -51,6 +51,7 @@ void __system_initSyscalls(void) tv->reent = _impure_ptr; tv->thread_ptr = NULL; tv->tls_tp = __tls_start-8; // ARM ELF TLS ABI mandates an 8-byte header + tv->srv_blocking_policy = false; u32 tls_size = __tdata_lma_end - __tdata_lma; if (tls_size) diff --git a/libctru/source/thread.c b/libctru/source/thread.c index 3c75b24..034a164 100644 --- a/libctru/source/thread.c +++ b/libctru/source/thread.c @@ -33,6 +33,7 @@ static void _thread_begin(void* arg) tv->reent = &t->reent; tv->thread_ptr = t; tv->tls_tp = (u8*)t->stacktop-8; // ARM ELF TLS ABI mandates an 8-byte header + tv->srv_blocking_policy = false; t->ep(t->arg); threadExit(0); }