/*
 * Copyright 2018-2024, ttcoder
 * All rights reserved. Distributed under the terms of the MIT license.
 */

/*
Overview:
	This is where we implement create_sem(), write_port() and other low level Haiku
	functions declared in the OS.h header. They basically delegate to <haiku_syscalls_srv_>.

Aliases:
	- libroot
	- pseudo-libroot
	- user/client side of syscalls
	- API primitives
*/


// genode/base.lib.a
#include <base/log.h>  // Genode::log()
#include <base/sleep.h> ///

// genode/libc
#include <libc/component.h>  // Libc::with_libc()

// libc.lib.so
#include <string.h>  // memcpy()
#include <stdio.h>  // printf()
#include <unistd.h>  // read/close()

// haiku.lib.so
#include <OS.h>
#include <image.h>
#include "kinda_serverside.h"

#if defined( HAS_BROKER )
#	include "RemoteBroker.h"
extern Remote * remote;
#endif



/**************/
static int debug = 0;
/*
Tracing levels
	1: thread creation
	2: port creation/deletion
	3: read/write port (completion only)
	4: read/write port (internals)
	6: find_thread()
*/
static int debug_sem = 0;
/*
Tracing levels
	2: semaphore create/del/acquire
*/



//#pragma mark - ::semaphores -


// see https://www.haiku-os.org/legacy-docs/bebook/TheKernelKit_Semaphores_Overview.html

sem_id create_sem( int32 initial_count, const char * name )
{
	if( initial_count < 0 )
	{
		Genode::error( "semaphore initialized with ", initial_count, " initial value -- yet as per the bebook, 'you can't initialize the thread count to less-than-zero'..");
		//
		return B_BAD_VALUE;  // BeBook: "Invalid thread_count value (less than 0)."
		//
	}
	
	sem_id result = haiku_syscalls_srv_
		.SemCreate( initial_count, name );
	
	if( debug_sem >= 2 )
		Genode::log( " ::Create_Sem( \"", name, "\" count=", initial_count, " ) => ", result );
	
	return result;
}

status_t delete_sem( sem_id id )
{
	if( debug_sem >= 2 )
		Genode::log( " ::Delete_Sem(", id, ")" );
	
	return haiku_syscalls_srv_
		.SemDelete( id );
}

status_t acquire_sem( sem_id id )
{
	if( debug_sem >= 2 )
		Genode::log( " ::acquire_sem(", id, ")" );
	
	return acquire_sem_etc( id, 1, 0, 0 );
}

status_t acquire_sem_etc(
		sem_id id,
		int32 count,
		uint32 flags,
		bigtime_t timeout
		)
{
///FIXME-2: investigate and fix, or remove this notice, re BPopUpMenu:
//(also causes trouble for BAlert? BAlert in "synchronous-from-BWindow-thread" mode will fail to call UpdateIfNeeded() indeed... but anyhow given our implementation, UpdateIfNeeded() would return() right away )
	/*
	There's suspicion that the lock-up that sometimes occur in Pulse, opening the popup to invoke the About box,
	might relate to acquire_sem_etc() : PopUpMenu.cpp calls acquire_sem_etc( B_TIMEOUT, 50000 )
		==> Maybe acquire_sem_etc() should return immediately in that case, instead of remaining stuck(?) ?
	Although I _never_ see the below ::warning() in Terminal, so maybe it's something else...
	
	UPD: mid-april 2023: fixed the more serious bug (never returning from BAlert at all) with the "semaphore fix" commit ; delete_sem() will now release the semaphore, allowing synchronous BAlerts to close
	*/
	
	if( debug_sem >= 2 )
		Genode::log( "  ::acquire_sem_etc(id=", id, " count=", count, " flags=", flags, " timeout=", timeout, ")" );
	
	if( count != 1 )
		Genode::error( "semaphore-acquire with count=", count, " IS N/I YET ********" );
	if( count < 1 )
		return B_BAD_VALUE;  // BeBook: "It's illegal to specify a count that's less than 1."
	
	if( flags || timeout )
	{
		if( flags != B_TIMEOUT || timeout != B_INFINITE_TIMEOUT )
			// we get this e.g. with BAlert: "warning: ... flags: 8 timeout: 50000"
			// we also get a lot of this with Deskbar
			// could be due to e.g. PopUpMenu.cpp:465 ... ?
			Genode::log( "  .. semaphore-acquire with flags or timeout is n/i yet --- flags: ", flags, " timeout: ", timeout );
		// else:
		// B_TIMEOUT -:- B_INFINITE_TIMEOUT is functionally equivalent to what we implement, so no reason for alarm
///FIXME-2: we do not implement the time-out and proceed below with the equivalent of an *infinite* timeout in SemAcquire() ; even if I can't implement an actual timeout, at least call SemGet(), and if we pre-detect the lock, return EAGAIN/B_WOULD_BLOCK ? Though that's open to a race...
	}
	
	sem_id result = EAGAIN;
	
	{
		result = haiku_syscalls_srv_
			.SemAcquire( id );
		
		switch( result )
		{
			///later: remove this 'case' once the EAGAIN code is completely removed from kinda_serverside.cpp
			case EAGAIN:
				Genode::error( "acquire_sem: EGAIN should no longer occur -- sleep_forever()" );
				Genode::sleep_forever();
			break;
			
			case B_OK:
				if( debug_sem >= 2 )
					Genode::log( "     acquire_sem_etc(", id, "): B_OK" );
				//
				return result;
				//
			
			case B_BAD_SEM_ID:
			default:
				if( debug_sem >= 2 )
					Genode::log( "     acquire_sem_etc(", id, "): ERROR: ", result );
				//
				return result;
				//
		}
	}
	
	return result;  // e.g. B_BAD_SEM_ID
}

status_t release_sem( sem_id id )
{
	if( debug_sem >= 2 )
		Genode::log( "  ::release_sem(", id, ")" );
	
	return haiku_syscalls_srv_
		.SemRelease( id );
}

status_t release_sem_etc( sem_id id, int32 count, uint32 flags )
{
	if( debug_sem >= 2 )
		Genode::log( "  ::release_sem_etc(", id, ") with count=", count );
	
	if( flags != 0 )
		Genode::warning( "semaphore-release with flag=", flags, " IS N/I YET **" );
	
	///later: release_sem_etc() is brute-force implemented, is that good enough? (first use-case seen in Vision-IRC, which calls us with COUNT=1000, for some sort of reader/writer scheme)
	for( int i = 0; i < count; i ++ )
	{
		auto res = haiku_syscalls_srv_.SemRelease( id );
		if( res != B_OK )
			return res;
	}
	
	return B_OK;
}

status_t get_sem_count( sem_id id, int32 * thread_count )
{
	if( debug_sem >= 2 )
		Genode::log( __func__, "(id=", id, ")" );
	
	return haiku_syscalls_srv_
		.SemGet( id, thread_count );
}



//#pragma mark - ::ports (support) -


#if defined( HAS_BROKER )

Remote::Remote( Genode::Env & env )
#if 0
:	outBuf()
	,tx( -1 )
	,rx( -1 )
#else
:	brokerConn( env )
#endif
	,numberingBase_Team( 0 )  // will remain 0 for "lo-tech" scenarios, will get assigned >1 in full-fledged HAS_BROKER runs
{
	// in the specific case of Broker itself using this code:
	// use lazy init, to avoid a deadlock attempting to sync-connect to itself?
	//openPipes();
	// nah, Broker NO LONGER uses this code, no longer links against haiku.lib.so !
	
	// So be deterministic now:
	ctorInit();
	//Genode::log( "Remote: CTor done, numberingBase_Team: ", PortNumberingBase() );//numberingBase_Team );
}


team_id Remote::PseudoTeam() const
{
	return numberingBase_Team;
}

int Remote::PortNumberingBase() const
{
	// !
	// team 1 will get ports numbered 1000-to-1999,
	// team 2 will get ports numbered 2000-to-2999 and so on
	// (couldn't think of a better scheme/field expedient solution just yet)
	return PseudoTeam();
}

void Remote::EnsureInitialized()
{
	//_d_:
	//if( !broker )
	//	lazyInit();
}

static
int func__read_from_broker( void * ptr )
{
	int rx_fd = 0;
	{
		rx_fd = (long)ptr;///
	}
	
	for( ; ; )
	{
		int port = -1;
		int code = -1;
		int size = -1;
		void * buf = NULL;
		{
			int got = 0;
//			int32 * pseudo_int = (int32*)(long)&buf[0];//
			
			if( debug )
				printf( "  inbound.read()\n" );
			
			// <port>:
			Libc::with_libc([&] () {
				got = read( rx_fd, &port, sizeof(port) );
			} );
			
			if( debug )
				printf( "  inbound.read: %d bytes for port %d\n", got, port );
			
	//////seems we get _0_ instead of _-1_ on a closed pipe ??
	//and even in 'normal' case, only 1 byte:
//printf("......... got %d bytes\n", got );
			if( got <= 0 ) break;
			if( got != sizeof(int32) ) { printf("unexpected read size %d, should I handle such 'fragmented' tx'es ?\n", got); continue; }
			
			// <code>:
			Libc::with_libc([&] () {
				got = read( rx_fd, &code, sizeof(code) );
			} );
			if( got <= 0 ) break;
			if( got != sizeof(code) ) { printf("unexpected read size %d, should I handle such 'fragmented' tx'es ?\n", got); continue; }
			
			// <size>:
			Libc::with_libc([&] () {
				got = read( rx_fd, &size, sizeof(size) );
			} );
			if( got <= 0 ) break;
			if( got != sizeof(size) ) { printf("unexpected read size %d, should I handle such 'fragmented' tx'es ?\n", got); continue; }
//printf("Header-elements retrieved from port: port %d size %d code %d\n", port, size, code);

			
			// !
#if 0
			char _buf[512] = {};
	if(size>sizeof(buf)) Genode::error( "size is too big for one-time read!" );
			got = read( rx_fd, buf, size );
			if( got <= 0 ) break;
			if( got != (signed)size ) { printf("unexpected read size %d, should I handle such 'fragmented' tx'es ?\n", got); continue; }
#else
			buf = malloc( size ); //calloc()? or memset to 0?
			//+if( buf )..
			{
				char * pos = (char*)buf;
				int remaining = size;
				
				while( remaining > 0 )
				{
					Libc::with_libc([&] () {
						got = read( rx_fd, pos, remaining );
					} );
					if( got <= 0 ) break;///report error ?
					
					pos += got;
					remaining -= got;
				}
			}
#endif
		}
		
		if( debug >= 2 )
			printf( "Port data retrieved, converting to WRITE_PORT, port %d code %d size %d\n", port, code, size );
		
		///(ok with MT safety ? is it ok to call write_port_etc() directly from this thread ?)
		auto res = write_port_etc( port, code, buf, size,
			0,0);
		if( res != 0 )
			Genode::warning( "  port/pipe read/conv: write_port <", port, "> res: ", res );
		
		// T+
		free( buf ); buf = NULL;
	}
	
	Genode::error( "read_from_broker: exited loop... broken pipe ? normal exit ? closed pipe ?" );
	
	close( rx_fd );
	
	return 0;
}


void Remote::lazyInit()//openPipes()
{
}

void Remote::ctorInit()
{
	//if( debug )
	//Genode::log( " > > > Remote: CTOR/Init/OpenPipes" );
	
#if 0
	// T-
	//
	
	rx = open( "/dev/downstream", O_RDONLY );
	if( rx < 0 )
	{
		Genode::error( "pipe from Broker: cannot open, status: ", rx );//return -1;//	CHECK( rx >= 0 );
		return;
	}
	
	tx = open( "/dev/upstream", O_WRONLY );
	if( tx < 0 )
	{
		Genode::error( "pipe to Broker: cannot open, status: ", tx );
		//Genode::error( "remote: open: status ", tx, " errno: ", errno );
		//return tx;///errno ?
		return;
	}
	
	// T+
	//puts( "HandShake......." );
	StartMessage( bro::HANDSHAKE );
	Flush();
	//puts( "HandShake sent, awaiting reply......." );
	numberingBase_Team =
		SyncReadInt32();

#else

	// Connect to our RPC server ('Broker') for outbound traffic.
	// As with all RPC usage, if we are launched before Broker, Genode will
	// make us gently wait until Broker is ready to answer RPC calls.
	// (if the run/config file calls for Broker usage -- if not, we will immediately fail cleanly).
	//

//_d_ (moved to HoG_init() now):
# if 0
	try
	{
		/**/ extern Genode::Env * HogEnv();
		broker = new bro_conn::Connection( *HogEnv() );
	}
	catch( ... )
	{
		Genode::error( "Broker not found: will operate with local-only kernel (no inter-app BMessage/port IPC)" );
		
		//
		return;
		//
	}
# endif
	
	//bro_conn::Session::PseudoTeam pt = numberingBase_Team;
	numberingBase_Team = brokerConn.id_for_team();
	
	long/*int*/ rx = 0;
	Libc::with_libc([&] () {
		rx = open( brokerConn.pipe_for_team().string(), O_RDONLY );
	} );
	
	if( debug )
		Genode::log( "HandShake with kernel/Broker done -- we are pseudo-team <", numberingBase_Team, "> with pipe <", brokerConn.pipe_for_team(), "> -- spawning reader" );
#endif
	
	// Connect/setup inbound traffic (via POSIX pipes)
	//
	thread_id th = spawn_thread(
		func__read_from_broker,
		"read_from_broker",
		1,
		(void*)rx//////ToDo: use a ptr-to-(malloc'ed)-struct or whatever...
		);
	///later: preserve <th> thread id somewhere, to kill it later on? though better have it auto-kill on close()ure of the pipe instead
	if( th < 0 )
		Genode::error( "*** cannot spawn_thread 'read_from_broker' : ", th );
	else
		resume_thread( th );
}


void Remote::NotifyPortCreated( port_id id, const char * name )
{
#if 0
	StartMessage( bro::CREATED_PORT );
	outBuf.AppendInt32( id );
	//RemoveString will need a fixed size, where this creates a tight-size, arghhh
	//outBuf.AppendString( name );  // Variable-length, so do this last !
	appendString32( name );
	
	//return status?
	Flush();
#else
	//if( !broker )
	//	lazyInit();
	
	// might fail altogether, test non-null:
	//if( NULL == broker )
	//	return;
	
	brokerConn.port_created( id, name );
#endif
}

port_id Remote::FindPort( const char * name )
{
#if 0
	///ToDo: implement with Genode * RPC * call, instead of using pipes !
	//The only call that must go through pipes is WRITE_PORT, because messages can be big...
	
	StartMessage( bro::FIND_PORT );
	//outBuf.AppendString( name );//Attach( name );
	appendString32( name );
	
	status_t res = Flush();
	//if err, return err
	if( res < 0 )
		return res;
	
	//sync read:
	return SyncReadInt32();
#else
	//if( !broker )
	//	lazyInit();
	
	// might fail altogether, test non-null:
	//if( NULL == broker )
	//	return -1;
	
	return brokerConn.find_port( name );
#endif
}

#if 0
void Remote::appendString32( const char * str )
{
	char buf[ B_OS_NAME_LENGTH ] = {};  // 32 bytes
	strlcpy( buf, str, sizeof(buf) );
	
	outBuf.AppendData( buf, sizeof(buf) );
}

int32 Remote::SyncReadInt32()
{
	// Rx from "server"
	
	int32 value = 0;
	
	//_d_ for( ; ; )
	{
		char buf[256] = {};
		const int got = read( rx, buf, sizeof(buf) );
		
		//if( got == 0 )
		//	break;
		if( got != sizeof(int32) )
		{
			Genode::error( "rx error: expected ", sizeof(int32), " bytes, got ", got, " bytes instead" );
			return B_ERROR;//break;
		}
		
		value = *((int32*)buf);
		Genode::log( "client rx Int32: ", value);//(const char*) buf );/**/
	}
	//CHECK(
	//move to Remote::dtor:    close(rx);
	// == 0 );
	
	return value;
}
#endif

status_t Remote::WritePort(
		port_id port,
		int32 code,
		const void * buffer,
		size_t size
		)
{
	if( debug )
		Genode::log( "remote.WritePort( ", port, ", ", code, " size=", size, " )" );
#if 0
	StartMessage( bro::WRITE_PORT );
	outBuf.AppendInt32( code );//	Attach<int32>( code );
	outBuf.AppendData( buffer, size );//	Attach( buffer, size );
	
	return Flush();
#else
	//if( !broker )
	//	lazyInit();
	
	// might fail altogether, test non-null:
	//if( NULL == broker )
	//	return -1;
	
	//_d_, old atomic (but payload-limited) call and code:
	//
	//	broker->write_port( port, code, (const char*)/**/buffer, size );
	/*
	bro_conn::Session::BufData bu;
	if( buffer )
		memcpy( bu.buf, buffer, size );
	
	// returns a status_t from the returned int:
	return broker->write_port( port, code, bu, size );
	*/
	
	// Thread-safety (handling concurrent threads and concurrent apps):
	//
	// Interaction between the Broker server and broker client code below is vulnerable, because
	// instead of just one atomic RPC call there are two: writeport_header() and writeport_chunk(),
	// so an RPC session might get two intermingled (non-sequentially ordered) "streams" of chunks.
	//
	// So we provide those calls with an id/discriminant so that they can sort out which
	// is which, and dispatch each stream's chunks into its own "accumulator" buffer.
	// That takes care of the "concurrent threads within a team" aspect. Note on combined
	// thread/team ID: technically, it would be enough for the ID to be that of the thread,
	// combining it with an ID of the app/team is not *needed* since different apps
	// end up in different RPC sessions/end-points, the problem is only with threads within a
	// given team, that end up into the same RPC ep (hence the risk of "multiplexing"/mixing up
	// chunks from 2 or more port transactions into one).
	// However, for debugging and tracing, it's handy to read about the app/team ID in tracing,
	// so we go fancy and do something shifting/or'ing to combine the team ID with the thread ID.
	// Note on ports: the Broker server does not create a super-discriminant based on
	// our thread-id and on the port, as it does not need to : it would be vulnerable
	// to inter-leaved data streams if we called writeport_chunk() on different ports
	// from a given thread here in the client code, but we never do that, we always
	// complete a 'stream' before starting a new (possibly to a different port) 'stream'.
	//
	// As to taking care of the "concurrent apps/teams": indeed, even if the apps are talking
	// to distinct sessions, the written data still ends up in the one recipient POSIX pipe,
	// but that aspect is taken care of because data writing is only done in one go, when
	// all the data is ready for write()ing all at once, and write() is (presumably) atomic,
	// so the pipe will be filled with complete "messages", no overlap.
	
	long discri = 0;
	{
		discri = PseudoTeam();  // optional, see above
		discri <<= 32;  // a team_id is 32 bits long
		discri |= find_thread( NULL );  // combine with the all-important thread_id
	}
	
	// writeport_header()
	//
	int status = brokerConn.writeport_header( discri, port, code, size );
	if( status < 0 )
		//
		return status;  // e.g. B_BAD_PORT
	
	if( NULL == buffer )
		// nothing more to do
		return B_OK;
	
#if 0
	{
		bro_conn::Session::BufData bu;
		memcpy( bu.buf, buffer, size );
		
		int sent = broker->writeport_chunk( port, bu, size );////
		if( sent < 0 )
			//
			return sent;
			//
		if( sent == 0 )
			Genode::error( "write_port: zero-size write ?!?" );
	}
#else
	size_t remaining = size;
	size_t done = 0;
	
	// writeport_chunk()
	//
	while( remaining > 0 )
	{
		bro_conn::Session::BufData bu;
		size_t chunk_size = remaining;
		{
			if( chunk_size > sizeof(bu.buf) )
				chunk_size = sizeof( bu.buf );
			memcpy( bu.buf, ((const char*)buffer) +done, chunk_size );
		}
		
		if( debug )
			Genode::log( "remote.write chunk" );
		
		// T0
		//
		int sent = brokerConn.writeport_chunk( discri, port, bu, chunk_size );
		if( sent < 0 )
			//
			return sent;
			//
		if( unsigned(sent) != chunk_size )
			Genode::error( "write_port: partial write ?! Should we bail out or what ?" );
		//++ or just use <sent> instead of <chunksize> down here:
		
		done += chunk_size;
		remaining -= chunk_size;
	}
	
	/*
	Real-world tracing example for the above notes:
	
	If writeport_*() worked on a unique buffer, we'd get a train wreck if e.g.
	Tracker and Deskbar called Registrar (on port 3000) via Broker-server in
	a non-sequential (but rather interleaved) fashion, which then manifests
	with e.g. Broker losing track of the fields it's reading and mis-reading
	a random field value as a "size" field and malloc()ing that bogus size:
		[init -> Deskbar] }
		[init -> broker_server] writeport_header <3000> <1886023792> 323 overall data bytes expected
		[init -> Tracker] }
		[init -> broker_server] writeport_header <3000> <1886023792> 323 overall data bytes expected
		[init -> broker_server] writeport_chunk <3000> 64 bytes in this one chunk
		.......
		[init -> broker_server] writeport_chunk <3000> 3 bytes in this one chunk
		[init -> broker_server] writeport_chunk <3000> 64 bytes in this one chunk
		[init -> broker_server] writeport_chunk <3000> 3 bytes in this one chunk
		.......
		[init] child "registrar" requests resources: ram_quota=16809984
	Here's it's from two teams, but it could even be two threads from a team.
	*/
#endif
	
	if( debug )
		Genode::log( "remote.write -> B_OK" );
	
	return B_OK;
#endif
}

#if 0
void Remote::StartMessage( int32 code )
{
	if( rx < 0 || tx < 0 )
		openPipes();
	
	if( debug )
		printf( "  .startmessage <%d>\n", code );
	
	outBuf.AppendInt32( code );
}

status_t Remote::Flush()
{
	if( debug >= 3 )
		puts( "    -------> Flush to Broker" );
	
	// Tx to "server"
	//
// not dispatch-pipe, our personal pipe instead!
	unsigned sent = write( tx, outBuf.Data(), outBuf.Size() );//buffer, size );
	if( sent != outBuf.Size() )
	{
		outBuf = BNetBuffer();//outBuf.Clear();
		
		Genode::error( "remote: write: status ", sent, " errno: ", errno );
		return B_ERROR;///?
	}
// move to Remote::dtor:	close( tx );//check == 0?
	
	outBuf = BNetBuffer();//outBuf.Clear();
	return B_OK;
}
#endif

#endif  // ~HAS_BROKER



//#pragma mark - ::ports -

/*
static
int port_numbering_base()
{
#if defined( HAS_BROKER )
	return remote.PortNumberingBase();
#endif
	//#else, no broker to global ports, only local ports, so start numbering at 0:
	return 0;
}
*/

port_id create_port( int32 /*capacity*/, const char * name )
{
	// param <capacity> is ignored -- in HoG we implement "infinite" capacity instead.
	
	int port_numbering_base = 0;  // local-only ports: start at 0
#if defined( HAS_BROKER )
	// T- (for PortCreate() to use the right numbering base/PortNumberingBase())
	if( remote )
	{
		remote->EnsureInitialized();
		// T0
		//Genode::log( "remote: create port: PortNumberingBase: ", remote->PortNumberingBase() );
		port_numbering_base = remote->PortNumberingBase();
	}
#endif  //  ~!HAS_BROKER
	
	// T0
	port_id port =
		haiku_syscalls_srv_
			.PortCreate( name, port_numbering_base );
	
#if defined( HAS_BROKER )
	// T+
	if( remote )
		remote->NotifyPortCreated( port, name );
#endif
	
	if( debug >= 2 )
		Genode::log( " ::Create_Port( ", name, " ) => __", port, "__" );
	
	return port;
}

status_t close_port( port_id port )
{
	// USED BY
	// BLooper/BWindow, to deny arrival of new (esp. "ping") messages, yet keep the port around for a bit to drain it
	
	if( debug >= 2 )
		Genode::log( "  ", __func__, "( ", port, " )" );
	
//
	haiku_syscalls_srv_
		.ReleaseBlockadeForPort( port );
		// in case someone was waiting on that (now closed) port : don't let them hang
	haiku_syscalls_srv_
		.PortClose( port );
	//
	//DONE
	//// move this _above_ portclose() ? Otherwise I get "Error: requesting blockade on a closed-down port ; makes little sense for someone to wait on new msgs that won't be allowed to arrive!"
	//// or release the blockade directly inside PortClose() itself, rather than here !
//	haiku_syscalls_srv_
//		.ReleaseBlockadeForPort( port );
		// in case someone was waiting on that (now closed) port : don't let them hang
	
	return -1;
}

status_t delete_port( port_id port )
{
	if( debug >= 2 )
		Genode::log( "  ", __func__, ": ", port );
	
	haiku_syscalls_srv_
		.PortDelete( port );
	haiku_syscalls_srv_
		.ReleaseBlockadeForPort( port );
		// in case someone was waiting on that (now deleted) port : don't let them hang.
		///ToDo: uh... the port is deleted right above this.. So either 1) release before deleting, or 2) remove altogether, since presumably a deleted blockade automatically "unblocks" listeners ?
	
	return -1;
}

status_t write_port_etc(
		port_id port,
		int32 code,
		const void * buffer,
		size_t size,
		uint32 flags,
		bigtime_t timeout
		)
{
	if( debug >= 3 )
	{
		if( 0==code && NULL==buffer && 0==size && 8==flags && 0==timeout )
			// this type of 'wake-up ping' is so common that it should get logged differently from a write with actual payload:
			Genode::log( "  ping port( ", port, " )" );
		else
			Genode::log( "  ", __func__, "( ", port, " ) code: ", code, " bytes: ", size, " flags: ", flags, " timeout: ", timeout );
	}
	
#if defined( HAS_BROKER )
#define IsRemote(x) (x >= 0 && (x/1000 != remote->PortNumberingBase()))
	if( remote && IsRemote(port) )
	{
		// Sanity check: might make sense to check this, since I never saw anything other than <0> (for "ping" messages), and post-msg-code (for sending archived BMessages):
		//if( code != kPortMessageCode && code != 0 )  // op-code *must* be 'pjpp' aka "flattened BMessage", there's nothing else out there anyway
		//	Genode::error( "write_port: unsupported/nonsensical code" );
		
		if( debug )
			Genode::log( "  write_port <", port, ">: this is a /remote/ (not local) port, let's leverage the IPC Broker" );// -- our pseudo-team/port-base: ", remote.PortNumberingBase() );
		
		return remote->WritePort( port, code, buffer, size );
	}
	// Else: this is either
	// - a local(ly initiated) write
	// - the "second half" h2 part of a remote(ly initiated) write
	// Hence our calling PortAppend():
#endif  // ~HAS_BROKER
	
///later: come to think of it, we do NOT care about timeouts, they only concern BeOS/Haiku, which might block when a port queue is full, but our queues are never full, so ignore timeouts altogether (e.g. Tracker calls for timeout '2500000'), and stop reporting/warning about them ?
	if( flags != 0 && flags != B_RELATIVE_TIMEOUT )
		Genode::error( "  write_port_etc with unknown flags: ", flags);
	if( timeout != 0 && timeout != 9223372036854775807 )  // we typically get "zero" or "infinite" timeout ; timeouts are no use here though... (port writing will never get 'stuck' waiting here in HoG, unlike in Haiku proper where it might run out of immediately available room)
		Genode::warning( "  write_port_etc with odd timeout: ", timeout);
	
	status_t status = haiku_syscalls_srv_
		.PortAppend( port, code, buffer, size );
	
///ToDo: before validating this "hush hush", investigate to find out *why* there  is so much bad-port traffic
//	if( status != B_OK )
	if( status != B_OK && status != B_BAD_PORT_ID )
		Genode::warning( "write_port error, status=", status );
	
	haiku_syscalls_srv_
		.ReleaseBlockadeForPort( port );
	
	return status;
}


ssize_t read_port(
		port_id port,
		int32 * code,
		void * buffer,
		size_t buffer_size
		)
{
	return read_port_etc(
		port, code, buffer, buffer_size,
		0, B_INFINITE_TIMEOUT
		);
}

ssize_t read_port_etc(
		port_id port,
		int32 * code,
		void * buffer,
		size_t size,
		uint32 flags,
		bigtime_t timeout
		)
{
	if( debug >= 3 )
		Genode::log( " ::read_port_etc( ", port, " ): start waiting [bufsize: ", size, " timeout: ", timeout, "]" );
	
	///+++ if flags or timeout, Genode::error..
	if( flags != 0 && flags != B_RELATIVE_TIMEOUT )//|| timeout )
		Genode::error( "  read_port_etc with extra params !");
	
	ssize_t
		result = -1;
	// loop, in case we get spurious wake-ups:
	do
	{
		// Port reading (unlike writing) never needs to be delegated to remote Broker:
		result = haiku_syscalls_srv_
			.PortRead( port, code, buffer, size );
		
		// size zero is *legal* -- in fact it's often used to 'ping' a message queue.
		if( result >= 0 )
			break;
		
		if( debug >= 4 )
			Genode::log( "read_port_etc( ", port, " ): blocking, nothing to return yet... flags=", flags, " timeout:", timeout );
		
///later: we are careful to only block waiting for port data on "infinite (absolute?) timeout", but not quite perfect yet?
		if( timeout != B_INFINITE_TIMEOUT )
			break;
		// else:
		
///ToDo: detect the "no such blockade" error condition, otherwise we loop endlessly !
		haiku_syscalls_srv_
			.WaitBlockadeForPort( port );
	}
	while( result < 0 );
	
	if( debug >= 3 )
		Genode::log( "  ::read_port_etc( port: ", port, " ) returns => ", result );
	
	// return actual size of retrieved payload
	return result;
}

ssize_t port_buffer_size_etc(
		port_id port,
		uint32 flags,
		bigtime_t timeout
		)
{
	if( debug >= 3 )
		Genode::log( " ::port_buffer_size( ", port, " ):" );
	
	if( flags != B_RELATIVE_TIMEOUT )// || timeout != 0 )
		Genode::warning( "unsupported Port Buffer Size flags:", flags );
	
	ssize_t
		result = -1;
	do
	{
		// Port reading (unlike writing) never needs to be delegated to remote Broker:
		result = haiku_syscalls_srv_
			.PortPeek( port, flags, timeout );
		
		if( result >= 0 )
			// found something to read !
			break;
		
		// Else: the port is empty; should we return or not:
		
		if( timeout <= 0 )
			// caller wants us to return immediately, even if empty-handed
			return B_WOULD_BLOCK;
		///?
		
		// Else: caller wants us to wait for something to arrive
		
		if( debug >= 4 )
			Genode::log( "port_buffer_size_etc() of port ", port, ": blocking, nothing to return yet... flags=", flags, " timeout:", timeout );
		
		if( timeout != B_INFINITE_TIMEOUT )
			// never met this case; let's make it obvious if it ever happens:
			Genode::error( "treating timeout as infinite even though it's just positive but less-than-infinite (port_buffer_size_etc)" );
		
		haiku_syscalls_srv_
			.WaitBlockadeForPort( port );
	}
	while( result < 0 );
	
	if( debug >= 3 )
		Genode::log( " ::port_buffer_size_etc( ", port, " ) returns: ", result );
	
	return result;
}

ssize_t port_count( port_id port )
{
	// Port reading (unlike writing) never needs to be delegated to remote Broker:
	ssize_t result =
		haiku_syscalls_srv_
			.PortCount( port );
	
	if( debug >= 3 )
		Genode::log( " ::port_count( ", port, " ) => ", result );
	
	return result;
}

status_t _get_port_info( port_id port, port_info * info, size_t /*size*/ )
{
	// USED BY:
	// - BMessenger.IsValid()
	// - BRoster::_InitMessenger() ...
	//
	// Implementing get_port_info() is important for e.g. BMessenger.IsValid() to work,
	// which in turn is important for BMenuItem::Install(), so that it won't reset
	// the Messenger(), so that items can Invoke() correctly.
	
	if( debug >= 4 )
		Genode::log( " ::_get_port_info <", port, ">" );
	
	if( NULL == info )
		return B_ERROR;
	
	///for a more simple implementation (if nobody really uses get_info params):
	// if( port_count(port) < 0 )
	//		return status;
	// else
	//		info.blah = 999;
	//		return B_OK
	
#if defined( HAS_BROKER )
	if( remote && IsRemote(port) )
	{
		// Remote port:
		
		if( debug >= 1 )
			Genode::log( "_get_port_info <", port, "> (N/I yet!): this is a /remote/ (not local) port, let's leverage the IPC Broker" );
		
		// we hardcode "yes the remote port does exist", for now:
		return B_OK;
///ToDo: actually implement Remote.get_port_info() ? Though it does not seem to be missing all that much yet...
//		*info = broker->getportinfo();
//		return B_OK-or-not;
	}
#endif
	
	// Local port:
	return
		haiku_syscalls_srv_
			.PortInfo( port, *info );
}

status_t set_port_owner( port_id /*port*/, team_id /*team*/ )
{
	//Genode::warning( " n/i set_port_owner port<", port, "> team <", team, ">" );
	
	// note: seems there is harm in not implementing this as there is no e.g. leak side effect;
	// the main usage is in Message.cpp, where it is quickly followed by a delete_port(), as verified with tracing enabled (int debug=2)
	// (so what is the point of set_port_owner(), if the port is quickly deleted anyway ?)
	// We just make sure to return B_OK to keep the messaging code happy, otherwise it upsets BMessage._SendMessage() which fails to work:
	
	return B_OK;
}


port_id find_port( const char * name )
{
#if defined( HAS_BROKER )
	if( debug >= 2 )
		Genode::log( " ::find_port( ", name, " )" );
	
	// Lookup (remote) port
	if( remote )
		return remote->FindPort( name );

#else
	
	// Local-only ports: does not make much sense to implement anything, since find_port() is used for finding "remote" ports
	if( debug >= 2 )
		Genode::warning( __func__, "( ", name, " ): n/i" );
#endif
	
	return -1;
}



//#pragma mark - ::threads -


thread_id  spawn_thread(
		thread_func func,
		const char * name,
		int32 priority,
		void * data
		)
{
	thread_id result = haiku_syscalls_srv_
		.ThreadCreate( func, name, data );
	
	if( debug )
		Genode::log( " ::Spawn_Thread( ", name, " prio: ", priority, " data*: ", data, " ) => ", result );
	
	return result;
}


status_t  resume_thread( thread_id thread )
{
	if( debug >= 2 )
		Genode::log( " ::", __func__, "( ", thread, " )" );
	
	return haiku_syscalls_srv_
		.ThreadStart( thread );
}

status_t suspend_thread( thread_id thread )
{
///later: there IS a pause()/resume() pair in Genode, in cpu_thread.h, and base/src/test/thread/main.cc shows how to use it
	if( debug )
		Genode::warning( " ::", __func__, "( ", thread, " ): not supported in Genode's base/thread.h !" );
	
	return B_ERROR;
}

thread_id find_thread( const char * name )
{
	if( debug >= 6 )
		Genode::log( __func__, " name: ", name );
	
	return haiku_syscalls_srv_
		.ThreadByName( name );
}

status_t rename_thread( thread_id, const char * )
{
///later: rename_thread(), for BWindow::_SetName()...
	//Genode::warning( __func__, " n/i" );
	
	return -1;
}

void exit_thread( status_t )
{
	hog::ThreadWithFunc * th = haiku_syscalls_srv_
		.LookupThread( find_thread(NULL) );
	if( ! th )
		//
		return;
		//
	
///FIXME-2: do implement this as: pthread_exit( th->pthreadID ) soon'ish !
/// there IS a pthread_exit() function, is it the right match ? What do I test with, PopUpMenu (via BLooper) ?
	Genode::warning( __func__, " n/i, using CancelAndMark() instead; found thread: ", th->name(), " id: ", th->PosixID() );//, " discardable: ", th->IsDiscardable() );
	th->CancelAndMark();
}

status_t wait_for_thread( thread_id thread, status_t * return_value )
{
	if( debug )
		Genode::log( " ::wait_for_thread(", thread, ")" );
	
	if( debug >= 6 )
	{
		// No big deal: the only in-tree class that actually uses <return_value> is BMediaRoster, and it's for logging purposes only
		///later-maybe: <return_value> handling: assuming this gets implemented anyway some day, how to do it? maybe Native_utcb * Thread::utcb() has something to do with it ? UPD: now that pthreads are used, that would be a pthread-related mechanism instead
		if( return_value )
			Genode::warning( "wait_for_thread() with param for storage of thread exit status, but exit status is Not Implemented in HoG" );
	}
	
	return haiku_syscalls_srv_
			.ThreadJoin( thread );
}

status_t _get_next_thread_info(
		team_id,
		int32 *,
		thread_info *,
		size_t
		)
{
	Genode::error( __func__, " n/i" );
	
	return -1;
}

///ToDo: for now, fill out info.team with ThreadList[0].thread-id; but when we go RPC, we need
// something else... Does RPC provide a way to identify _who_ is making the RPC call to us ?
//UPD: we can rely on remote->PseudoTeam(), probably good enough:
status_t _get_thread_info(
		thread_id thread,
		thread_info * info,
		size_t
		)
{
	if( debug >= 6 )
		Genode::log( __func__, " n/i" );
	
	///later: _get_thread_info() should check that thread <thread> does exist ; returning B_OK unconditionally does not seem to hurt currently tho ; also what about filling-out the "name" field ?
	
	if( remote )
	{
		if( debug )
			Genode::log( " ............. team <", remote->PortNumberingBase(), "> for thread <", thread, ">" );
		
		info->team = remote->PseudoTeam();
	}
	else
		// "local-only kernel" mode : each team lives in an isolate silo, believing it's the one and only "team 0" out there:
		info->team = 0;///ToDo: refactor with a constant like "BROKERLESS_MODE_UNIVERSAL_TEAM" or such
	
	return B_OK;
}



//#pragma mark - ::images


status_t get_image_symbol(
	image_id ,
	const char *,
	int32 ,
	void **
	)
{
	if( debug >= 3 )
		Genode::warning( __func__, " n/i" );
	
	return -1;
}

status_t _get_image_info( image_id /*image*/, image_info * /*info*/, size_t /*size*/ )
{
	if( debug >= 3 )
		Genode::warning( __func__, " n/i" );
	
	return -1;
}

status_t _get_next_image_info(
		team_id,
		int32 *,
		image_info *,
		size_t
		)
{
	Genode::log( __func__, ": n/i" );
	
	return -1;
}

image_id load_add_on( const char * s )
{
	if( debug >= 2 )
		Genode::warning( " ::load_add_on: ", s );
	
	return -1;
}

status_t unload_add_on( image_id /*image*/ )
{
	Genode::warning( __func__, " n/i" );
	
	return -1;
}



//#pragma mark - ::areas


area_id  clone_area(
		const char *,
		void **,
		uint32,
		uint32,
		area_id
		)
{
	Genode::error( __func__, " n/i" );
	return -1;
}

status_t  delete_area( area_id )
{
	Genode::error( __func__, " n/i" );
	return -1;
}

status_t  set_area_protection( area_id, uint32 )
{
	Genode::error( __func__, " n/i" );
	return -1;
}

status_t  _get_area_info(
		area_id,
		area_info *,
		size_t
		)
{
	Genode::error( __func__, " n/i" );
	return -1;
}



//#pragma mark - for BMenuBar only...


// a (very) quick and (very) dirty implementation, since this
// little-used BeOS/Haiku API is just used in BMenuBar.. In fact
// we don't even bother with enforcing <thread> in send_data()
//
static bool new_thread_data_available_ = false;
static char thread_data_[32] = {};

status_t send_data(
		thread_id thread,
		int32 code,
		const void * buffer,
		size_t buffer_size
		)
{
	if( debug )
		Genode::warning( __func__, " to thread: ", thread, " code: ", code, " buf: ", buffer, " size: ", buffer_size );
	
	if( code != 0 || buffer_size != sizeof(thread_data_) )
	{
		Genode::error( "send_data: not the expected/supported arguments (somebody else than BMenuBar using this API?)" );
		return -1;
	}
	
	memcpy( thread_data_, buffer, buffer_size );
	new_thread_data_available_ = true;
	
	return B_OK;
}

int32 receive_data(
		thread_id * /*sender*/,
		void * buffer,
		size_t buffer_size
		)
{
	if( debug )
		Genode::warning( __func__, " by thread: ", find_thread(NULL) );
	
	if( buffer_size != sizeof(thread_data_) )
	{
		Genode::error( __func__, " to ", buffer, " size: ", buffer_size, "=> not the expected/supported args!" );
		return -1;
	}
	
	while( false == new_thread_data_available_ )
	{
///later:  send_data(): snooze/polling: can probably do better than that, even if this is a "little used API". Though judging by the below trace, seems we only ever do *one* iteration, so very low priority task...
//		Genode::log( " menu/receive_data(): snooze..." );
		snooze( 9000 );
	}
	
	memcpy( buffer, thread_data_, buffer_size );
	new_thread_data_available_ = false;
	
	return buffer_size;//?
}



//#pragma mark - for Vision etc...


ssize_t wait_for_objects_etc( object_wait_info * infos, int numInfos, uint32 flags, bigtime_t timeout)
{
	///ToDo: wait_for_objects_etc() assert: probably breaks Vision? implement with busy-waiting polling for now ?
	Genode::error( "wait_for_objects_etc: n/i, sleeping forever" );
	Genode::sleep_forever();
	return -1;
}


