/* $Id$ | |
* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to you under the Apache License, Version | |
* 2.0 (the "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/* | |
* xmpl_perf_helper.c | |
* transport helper for perf service | |
*/ | |
#include "etch_apr.h" | |
#include "xmpl_perf_helper.h" | |
#include "xmpl_perf_valufact.h" | |
#include "xmpl_perf_client_impl.h" | |
#include "etch_svcobj_masks.h" | |
#include "etch_global.h" | |
#include "etch_url.h" | |
#include "etchlog.h" | |
#include <tchar.h> | |
#include <stdio.h> | |
#include <conio.h> | |
char* ETCHETCH = "ETCH"; | |
char* ETCHTHIS = "PERF"; | |
char* ETCHCVER = "etch C v0.5.000"; | |
/** | |
* new_remote_server() | |
* this is java binding's helper.newServer(uri, rex, implfactory); | |
* constructs a client side remote server per specs in the supplied uri. | |
* generally invoked from a run thread in the client main .exe. | |
* RemotePerfServer server = PerfHelper.newServer( uri, null, implFactory ); | |
* @param uri | |
* @param p client parameters including callback to client ctor, | |
* caller reliquishes this memory. | |
* @return the new remote server object. | |
*/ | |
perf_remote_server* new_remote_server (wchar_t* uri, etch_client_factory* p) | |
{ | |
i_perf_client* myclient = NULL; | |
perf_client_stub* client_stub = NULL; | |
i_delivery_service* deliverysvc = NULL; | |
perf_remote_server* remote_server = NULL; | |
perf_value_factory* vf = new_perf_value_factory(); | |
ETCH_ASSERT(vf && p); | |
p->in_valufact = (etch_value_factory*) vf; | |
p->in_resx = etch_transport_resources_init(p->in_resx); | |
ETCH_ASSERT(p->in_resx); | |
etch_resources_add (p->in_resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) vf); | |
/* instantiate a delivery service */ | |
deliverysvc = new_etch_transport (uri, (etch_factory_params*) p, NULL); | |
ETCH_ASSERT(is_etch_ideliverysvc(deliverysvc)); /* TODO exception not assert */ | |
p->dsvc = deliverysvc; | |
/* instantiate the remote server */ | |
remote_server = new_perf_remote_server (NULL, deliverysvc, (etch_value_factory*) vf); | |
ETCH_ASSERT(is_etch_remote_server(remote_server)); /* TODO return exception or return null */ | |
p->server_id = remote_server->server_base->server_id; | |
p->server = remote_server; | |
remote_server->client_factory = p; /* assume ownership */ | |
/* here we call back to the client constructor in [main]. the purpose of the | |
* callback is to isolate the editable xxxx_client_impl constructor from the | |
* private constructor pieces. the callback instantiates a client implenentation | |
* and returns an interface to it. | |
*/ | |
myclient = p->new_client? p->new_client (remote_server): NULL; | |
ETCH_ASSERT(is_etch_client_base(myclient)); /* TODO return exception or return null */ | |
p->iclient = myclient; | |
p->fpool = (etch_threadpool*) etch_resources_get (p->in_resx, ETCH_RESXKEY_POOLTYPE_FREE); | |
p->qpool = (etch_threadpool*) etch_resources_get (p->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED); | |
ETCH_ASSERT(p->fpool && p->qpool); | |
/* construct stub */ | |
client_stub = new_perf_client_stub (p); | |
ETCH_ASSERT(is_etch_client_stub(client_stub)); | |
p->stub = client_stub; | |
return remote_server; | |
} | |
/** | |
* new_perf_listener() | |
* invoked from [main] to construct the session listener. | |
* this function is simply a proxy providing additional parameters | |
* to new_etch_listener, in order to simplify the call in [main]. | |
* @param main_new_server_callback pointer to [main]'s new_xxx_server() | |
* @return i_sessionlistener interface to listener. note that the returned | |
* listener.thisx is the specific server object, e.g. etch_tcp_server. | |
*/ | |
i_sessionlistener* new_perf_listener (wchar_t* uri, main_new_server_funcptr main_new_server) | |
{ | |
i_sessionlistener* listener = NULL; | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "instantiating main listener ...\n"); | |
listener = new_etch_listener (uri, NULL, new_helper_accepted_server, | |
main_new_server, new_perf_resources); | |
if (listener) | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "main listener instantiated\n"); | |
else | |
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not instantiate main listener\n"); | |
return listener; | |
} | |
/** | |
* start_perf_client() | |
* start the remote server, blocking until it comes up or errors out. | |
* @param uri the etch-formatted URI. | |
* @param new_client a pointer to a callback in the main client module | |
* which constructs the client implementation object and returns an interface | |
* to it. | |
* @param waitupms how long to wait for a connection to server to complete. | |
* @return the remote server, or null indicating error. | |
* todo return exception in remote server rather than null. | |
*/ | |
perf_remote_server* start_perf_client (wchar_t* uri, | |
new_client_funcptr new_client, const int waitupms) | |
{ | |
int result = 0; | |
perf_remote* remotebase = NULL; | |
perf_remote_server* remote = NULL; | |
etch_client_factory* impl_factory = NULL; | |
ETCH_ASSERT(new_client); | |
etchlog_open_client(); /* force logger to use client filename */ | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "creating perf client ...\n"); | |
impl_factory = new_client_factory (NULL, NULL, new_client); | |
ETCH_ASSERT(impl_factory); | |
/* instantiate a remote server, which invokes client constructor */ | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "instantiating remote server ...\n"); | |
remote = new_remote_server (uri, impl_factory); | |
ETCH_ASSERT(remote); /* todo exception rather than assert */ | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG,"remote server instantiated\n"); | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "perf client created\n"); | |
remotebase = remote->remote_base; | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "starting perf client ...\n"); | |
/* fyi call sequence here is as follows: | |
* etchremote_start_waitup() | |
* etchremote_transport_control() | |
* tcpdelsvc_transport_control() | |
* pmboxmgr_transport_control() | |
* msgzr_transport_control() | |
* pktzr_transport_control() | |
* tcpconx_transport_control() | |
* tcpconx_start() | |
* tcpconx_open() | |
*/ | |
/* BLOCK here until the connection to server comes up or times out */ | |
result = remotebase->start_waitup (remotebase, waitupms); | |
if (0 != result) | |
{ /* todo return exception with actual error in remote, rather than null */ | |
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not start perf client\n"); | |
remote->destroy(remote); | |
remote = NULL; | |
} | |
else etchlog (ETCHTHIS, ETCHLOG_INFO, "perf client started\n"); | |
return remote; | |
} | |
/** | |
* stop_perf_client() | |
* stop the remote server, blocking until it comes down or errors out. | |
* @return 0 success, -1 failure | |
* TODO return exception rather than result code. | |
*/ | |
int stop_perf_client (perf_remote_server* remote, const int waitdownms) | |
{ | |
int result = 0; | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "stopping perf client ...\n"); | |
/* fyi call sequence here is as follows: | |
* etchremote_stop_waitdown() | |
* etchremote_transport_control() | |
* tcpdelsvc_transport_control() | |
* pmboxmgr_transport_control() | |
* msgizer_transport_control() | |
* pktizer_transport_control() | |
* tcpconx_transport_control() | |
* tcpclient_stop_listener() | |
* tcpconx_closex() | |
*/ | |
result = remote->remote_base->stop_waitdown (remote->remote_base, waitdownms); | |
if (0 == result) | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "perf client stopped\n"); | |
else etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not stop perf client\n"); | |
return result; | |
} | |
/** | |
* run_perf_listener() | |
* start the main (accept) listener, block until listener exit, | |
* and finally tear down this server's outstanding client sessions. | |
* @return 0 success, -1 failure. | |
*/ | |
int run_perf_listener (i_sessionlistener* listener, const int waitupms) | |
{ | |
int result = 0; | |
if (NULL == listener) return -1; | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "starting main listener ...\n"); | |
/* call through to e.g. etch_tcpsvr_transport_control() to start listener */ | |
result = listener->transport_control (listener->thisx, | |
new_etch_event(CLASSID_CONTROL_START_WAITUP, waitupms), NULL); | |
if (0 == result) | |
{ etchlog (ETCHTHIS, ETCHLOG_INFO, "main listener started on thread %d\n", | |
transport_thread_id (listener)); | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "listening for connect requests ...\n"); | |
/* BLOCK here until listener thread exits (etch_listener_waitfor_exit) */ | |
result = listener->wait_exit (listener); | |
etchlog (ETCHTHIS, ETCHLOG_INFO, "main listener ended\n"); | |
if (transport_session_count (listener) > 0) | |
{ | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "begin client sessions teardown\n"); | |
result = transport_teardown_client_sessions (listener); | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "end client sessions teardown\n"); | |
} | |
} | |
else | |
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not start main listener\n"); | |
return result; | |
} | |
/** | |
* new_perf_resources() | |
* callback from new_etch_listener to initialize service-specific resources. | |
* @param p the etch_server_factory to be initialized | |
* @return 0 success, -1 failure. | |
*/ | |
int new_perf_resources (etch_server_factory* p) | |
{ | |
int result = 0; | |
ETCH_ASSERT((NULL != p) && (NULL == p->in_valufact)); | |
ETCH_ASSERT (p->in_resx && is_etch_hashtable(p->in_resx)); | |
p->in_valufact = (etch_value_factory*) new_perf_value_factory (); | |
ETCH_ASSERT(p->in_valufact); | |
if (NULL == p->in_valufact) return -1; | |
result = etch_resources_add (p->in_resx, | |
ETCH_RESXKEY_MSGIZER_VALUFACT, (objmask*) p->in_valufact); | |
ETCH_ASSERT(0 == result); | |
return result; | |
} | |
/** | |
* new_helper_accepted_server() | |
* this is java binding's newServer(). | |
* constructs the server side listener component of a remote client. | |
* invoked from the server's accepted connection handler, such as | |
* tcpxfact_session_accepted(), to create the remote client and stub | |
* with which to communicate with the individual client. | |
* we see then that each client has its own listener thread on the server, | |
* as opposed to a client ID scheme where all clients would filter through | |
* a single socket on the server. | |
* @param p parameter bundle, including the callback to the to the service- | |
* specific new server constructors in [main]. caller relinquishes. | |
* @return the client's service-specific server stub. | |
*/ | |
void* new_helper_accepted_server (etch_server_factory* p, etch_session* session) | |
{ | |
i_perf_server* iserver; | |
perf_server_stub* stub; | |
perf_remote_client* client; | |
ETCH_ASSERT(p && p->helper_new_accepted && p->main_new_server); | |
ETCH_ASSERT(p->in_resx && p->in_valufact); // TODO assert delivery service | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "instantiating accepted client listener ...\n"); | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "creating remote client...\n"); | |
client = new_remote_client (NULL, session, p->in_valufact); | |
client->session_id = session->session_id; | |
session->client = client; | |
/* here we CALL BACK to the constructor in [main], the purpose of the callback | |
* being to isolate the editable constructor from the private constructor. | |
* the callback instantiates a client's server implementation and returns | |
* an interface to it. | |
*/ | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "creating server implementation ...\n"); | |
iserver = p->main_new_server (p, session); | |
iserver->session_id = session->session_id; | |
session->server = iserver; | |
/* note that the main listener will use p->mainpool as a thread manager, not these */ | |
p->qpool = (etch_threadpool*) etch_resources_get(p->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED); | |
p->fpool = (etch_threadpool*) etch_resources_get(p->in_resx, ETCH_RESXKEY_POOLTYPE_FREE); | |
/* eventually new_perf_server_stub() gets to stub_base constructor, which sets | |
* the delivery service's session to this, the server stub. so, in the java binding, | |
* the server stub is referenced as delivery service.session. we should perhaps also | |
* store the stub opaquely in both the client and listener objects. | |
*/ | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "creating server stub ...\n"); | |
stub = new_perf_server_stub (p, session); | |
stub->session_id = session->session_id; | |
session->server_stub = stub; | |
if (iserver && stub) | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "accepted client listener instantiated\n"); | |
else | |
etchlog (ETCHTHIS, ETCHLOG_ERROR, "could not instantiate accepted client listener\n"); | |
return stub; | |
} | |
/** | |
* get_perf_client_impl() | |
* convenience to extract the client impl from the remote server object. | |
*/ | |
perf_client_impl* get_perf_client_impl (perf_remote_server* rs) | |
{ | |
i_perf_client* iclient = rs->client_factory->iclient; | |
perf_client_impl* pci = iclient? iclient->thisx: NULL; | |
return pci; | |
} | |
/** | |
* get_perf_client_stub() | |
* convenience to extract the client stub from the remote server object. | |
*/ | |
perf_client_stub* get_perf_client_stub (perf_remote_server* rs) | |
{ | |
perf_client_stub* stub = (perf_client_stub*) rs->client_factory->stub; | |
ETCH_ASSERT(is_perf_client_stub(stub)); | |
return stub; | |
} | |
/** | |
* get_perf_client_stubbase() | |
* convenience to extract the client stub base from the remote server object. | |
*/ | |
etch_stub* get_perf_client_stubbase (perf_remote_server* rs) | |
{ | |
perf_client_stub* stub = get_perf_client_stub (rs); | |
etch_stub* base = stub->stub_base; | |
ETCH_ASSERT(is_etch_stub(base)); | |
return base; | |
} | |
/** | |
* perf_client_cleanup() | |
* destroy instantiations from [main] | |
*/ | |
void perf_client_cleanup (perf_remote_server* remote) | |
{ | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "destroying remote server ...\n"); | |
ETCHOBJ_DESTROY(remote); | |
etchlog (ETCHTHIS, ETCHLOG_DEBUG, "remote server destroyed\n"); | |
} | |
/** | |
* etch_init() | |
* do client-side etch runtime initialization and service-specific initialization. | |
* this is intended as a convenience function to be invoked only by user client code. | |
*/ | |
int etch_init() | |
{ | |
return perf_runtime_init(INIT_ETCH_CLIENT); | |
} | |
/** | |
* etch_exit() | |
* etch runtime teardown plus service-specific teardown | |
* this is intended as a convenience function to be invoked only by user client code. | |
*/ | |
int etch_exit() | |
{ | |
return perf_runtime_exit(); | |
} | |
/** | |
* perf_runtime_exit() | |
* do etch runtime teardown plus service-specific teardown. | |
*/ | |
int perf_runtime_exit () | |
{ | |
etchvf_free_builtins(); /* TODO move this somewhere else */ | |
exitparams.is_show_leak_detail = config.is_log_memory_leak_detail; | |
etch_runtime_exit (&exitparams); | |
printf("\n%s exit\n", ETCHCVER); | |
return 0; | |
} | |
/** | |
* perf_runtime_init() | |
* do etch runtime initialization plus service-specific initialization. | |
*/ | |
int perf_runtime_init (const int is_client) | |
{ | |
printf("%s %s start\n", ETCHCVER, is_client? "client": "server"); | |
return etch_runtime_init_all (is_client); | |
} | |