You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@etch.apache.org by jd...@apache.org on 2009/04/22 19:25:51 UTC

svn commit: r767594 [41/43] - in /incubator/etch/trunk/binding-c/runtime/c: ./ ext/ ext/hashtab/ ext/lib/ inc/ lib/ project/ project/$etchstop/ project/bin/ project/etch/ project/logcli/ project/logsrv/ project/notes/ project/test/ project/test/logcli/...

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_client_tests.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_client_tests.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_client_tests.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_client_tests.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,1410 @@
+/* $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_client_tests.c 
+ * test code invoked from [main]
+ */
+
+#include "xmpl_perf_client_main.h"
+
+char* ETCHTEST = "TEST";
+
+
+/**
+ * perftestobj
+ * perftest "class" instance data
+ */
+typedef struct perftestobj
+{
+    unsigned sig;
+    char* descr;
+    wchar_t* uri;
+    int   waitms;
+    int   runtime;
+    int   count;
+    int   threads;
+    int   iterations;
+    new_client_funcptr new_client; /* user's main() client constructor */
+
+} perftestobj;
+
+int testerrors;
+etch_objsession_objinfo notifyinfo;
+typedef int (*perftest_threadproc) (etch_threadparams*);
+#define EXCEPTION_EXPECTED 1
+#define NOTIFY_EXPECTED    2
+#define PERFTESTOBJ_SIGNATURE 0xabaddeed
+
+
+/* - - - - - - - - - - - - - - -  
+ * support methods for tests
+ * - - - - - - - - - - - - - - -  
+ */
+
+/**
+ * perf_xmpl_log_startmsg()
+ * log start of individual test. 
+ */
+void perf_xmpl_log_startmsg (char* msgdescr)
+{
+    etchlog(ETCHTEST, ETCHLOG_INFO, "begin perf test message %s\n", msgdescr);
+}
+
+/**
+ * perf_xmpl_log_endmsg()
+ * log end of individual test.  
+ */
+void perf_xmpl_log_endmsg (char* msgdescr, const int result)
+{
+    etchlog(ETCHTEST, ETCHLOG_INFO, "end message %s result %d\n", msgdescr, result);
+}
+
+
+/**
+ * perf_xmpl_log_startmsgex()
+ * log start of individual test. 
+ */
+void perf_xmpl_log_startmsgex (char* msgdescr, const int count)
+{
+    etchlog(ETCHTEST, ETCHLOG_INFO, "begin %s count %d\n", msgdescr, count);
+}
+
+/**
+ * perf_xmpl_log_endmsgex()
+ * log end of individual test.  
+ */
+void perf_xmpl_log_endmsgex (char* msgdescr, const int result, const int count)
+{
+    etchlog(ETCHTEST, ETCHLOG_INFO, "end %s count %d result %d\n", msgdescr, count, result);
+}
+
+/**
+ * perf_xmpl_log_objerr()
+ * log unexpected test response object type.  
+ */
+void perf_xmpl_log_objerr (char* msgdescr)
+{
+    etchlog(ETCHTEST, ETCHLOG_ERROR, "response for %s was not an expected object type\n", msgdescr);
+    testerrors++;
+}
+
+
+/**
+ * perf_xmpl_log_resulterr()
+ * log unexpected response object value. 
+ */
+void perf_xmpl_log_resulterr (char* msgdescr)
+{
+    etchlog(ETCHTEST, ETCHLOG_ERROR, "result value for %s was not the expected value\n", msgdescr);
+    testerrors++;
+}
+
+
+/**
+ * perf_xmpl_log_resultok()
+ * log expected response received  
+ */
+void perf_xmpl_log_resultok (char* msgdescr, char* resultdescr)
+{
+    etchlog(ETCHTEST, ETCHLOG_INFO, "%s got expected result '%s'\n", msgdescr, resultdescr);
+}
+
+
+/**
+ * perf_xmpl_log_resultokex()
+ * log expected response received  
+ */
+void perf_xmpl_log_resultokex (char* msgdescr)
+{
+    etchlog(ETCHTEST, ETCHLOG_INFO, "%s got expected result\n", msgdescr);
+}
+
+
+/**
+ * perf_xmpl_validate_response_object()
+ * test response object for exception and log result.  
+ */
+int perf_xmpl_validate_response_object (objmask* responseobj, const int is_excp_expected)
+{
+    int result = -1;
+
+    if (is_excp_expected == NOTIFY_EXPECTED)
+    {
+       /* we need to let the receive thread run here, otherwise the session_notify()
+        * delivering the exception has not been called yet. this is a race condition
+        * in the java code, subsequently translated here. todo come up with a synch-
+        * ronization mechanism that will force the remote call to complete through
+        * either mailbox post and read, or session_notify(). */
+        etch_sleep(35);  /* fyi 20ms is not enough */
+
+        if (notifyinfo.is_exception) 
+        {  
+            /* exception from 1-way message came back via overridden session_notify() */
+            etchlog(ETCHTEST, ETCHLOG_ERROR, "user got expected exception response\n");
+            result = 0;
+        }
+        else
+            etchlog(ETCHTEST, ETCHLOG_ERROR, "missing session_notify exception\n");
+    }
+    else
+    if (NULL == responseobj) 
+        etchlog(ETCHTEST, ETCHLOG_ERROR, "user got null response\n");
+    else
+    if (is_exception(responseobj))
+    {   
+        etchexception* x = get_exception_from (responseobj);
+
+        if (NULL == x)
+            etchlog(ETCHTEST, ETCHLOG_ERROR, "user got malformed exception\n");
+        else
+        {   char* txta = x->ansitext? x->ansitext: "<no text>";
+            if (is_excp_expected) 
+            {   etchlog(ETCHTEST, ETCHLOG_ERROR, "user got expected exception '%s'\n", txta);
+                result = 0;
+            }
+            else 
+                etchlog(ETCHTEST, ETCHLOG_ERROR, "user got exception response '%s'\n", txta);
+        }
+    }
+    else result = is_excp_expected? -1: 0;
+
+    return result;
+}
+
+
+/**
+ * perf_xmpl_wait_seconds()
+ * sleep for nseconds, displaying a dot either every second or every five seconds. 
+ */
+void perf_xmpl_wait_seconds (int nseconds)
+{
+    int is_showdot = 0;
+    const int is_longwait = nseconds > 20;
+    if (nseconds <= 0) return;
+    etchlog(ETCHTEST, ETCHLOG_INFO, "sleeping %d seconds per config ", nseconds);
+
+    while(nseconds--) 
+    { etch_sleep(1000); 
+      is_showdot = is_longwait? nseconds % 5 == 0: TRUE; 
+      if (is_showdot) printf("."); 
+    }
+
+    printf("\n");
+}
+
+
+etch_session_notify  old_session_notify;
+
+void my_reset_notifyinfo() { memset(&notifyinfo, 0, sizeof(etch_objsession_objinfo)); }
+
+
+/**
+ * etchsession_my_session_notify()
+ * override for client impl session_notify.
+ * this override will receive notifications of unsolicited messages from server,
+ * in particular exceptions returned from one-way messages. 
+ * @param obj caller i_objsession*.
+ * @param evt some etch object or null. relinquished and destroyed here.
+ */
+int my_session_notify  (void* obj, void* evt)
+{
+    etchlog(ETCHTEST, ETCHLOG_DEBUG, "user received session_notify\n");
+    etchsession_get_objinfo (&notifyinfo, evt);
+    return old_session_notify (obj, evt);  /* destroys evt object */
+}
+
+
+/**
+ * override_client_session_notify()
+ * override the client's default session_notify to be our own callback,
+ * which saves the properties of the notified object prior to destroying the object.
+ */
+void my_override_client_session_notify (perf_remote_server* rs)
+{
+    etchlog(ETCHTEST, ETCHLOG_DEBUG, "user overrides session_notify\n");
+    old_session_notify = perf_remote_set_session_notify (rs, my_session_notify);
+    assert(old_session_notify);
+}
+
+
+/**
+ * restore_client_session_notify()
+ * restore the client's default session_notify to be the default callback,
+ */
+void my_restore_client_session_notify (perf_remote_server* rs)
+{
+    assert(old_session_notify);
+    etchlog(ETCHTEST, ETCHLOG_DEBUG, "user restores session_notify\n");
+    perf_remote_set_session_notify (rs, old_session_notify);
+}
+
+
+/**
+ * perftest_get_params()
+ * extract the perftest params from the etch thread params and validate 
+ */
+perftestobj* perftest_get_params(void* p)
+{
+    perftestobj* outobj = NULL;
+    if (p && ((etch_threadparams*) p)->signature == ETCH_THREADPARAMS_SIGNATURE)
+        outobj = ((etch_threadparams*) p)->data;
+    assert(outobj && outobj->sig == PERFTESTOBJ_SIGNATURE);
+    return outobj;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   
+ * perftest_threadprocs for each server-directed service method test
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    
+ */
+
+/* - - - - - - - - -  
+ * perf.add()
+ * - - - - - - - - -  
+ */
+
+/**
+ * perftest_threadproc_add
+ * perftest thread procedure for specific perf test "add"
+ */
+int perftest_threadproc_add (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+    char* thistest = "perf.add";
+
+    /* create parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */  
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  /* execute perf.add() n times */
+    {
+        etch_int32* resultobj = remote->add (remote, new_int32(1000000000), new_int32(2000000000));
+
+        if (0 == (result = perf_xmpl_validate_response_object ((objmask*)resultobj, 0)))
+            if  (is_etch_int32(resultobj))
+                 if  (resultobj->value == 3000000000)
+                      perf_xmpl_log_resultokex(thistest);
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        ETCHOBJ_DESTROY(resultobj);
+    }
+
+    /* stop and dispose of remote server */
+    /* note that if we omit the stop_waitdown() here, behavior is unchanged */
+    result = remote->remote_base->stop_waitdown (remote->remote_base, p->waitms);
+    assert(0 == result);
+
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/**
+ * perftest_threadproc_add_async
+ * perftest thread procedure for specific perf test "add"
+ */
+int perftest_threadproc_add_async (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+
+    /* create parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  
+    {   
+        etch_int32 *resultobj = NULL;
+        i_mailbox* mbox = NULL;
+
+        mbox = remote->async_begin_add (remote, new_int32(1000000000), new_int32(2000000000));
+        assert(mbox);   
+         
+        resultobj = remote->async_end_add (remote, mbox); 
+        assert(resultobj); 
+        
+        ETCHOBJ_DESTROY(resultobj);
+        perf_remote_dispose_mailbox (remote, &mbox);
+    }
+
+    /* dispose of server */
+    result = remote->remote_base->stop_waitdown (remote->remote_base, p->waitms);
+    assert(0 == result);
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/* - - - - - - - - -  
+ * perf.sum()
+ * - - - - - - - - -  
+ */
+
+/**
+ * perftest_threadproc_sum
+ * perftest thread procedure for specific perf test "sum"
+ */
+int perftest_threadproc_sum (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+    int values[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+    const int numdims = 1, itemsize = sizeof(int), itemcount = sizeof(values)/itemsize;
+    char* thistest = "perf.sum";
+
+    /* create parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  /* execute perf.sum() n times */
+    {   
+        etch_nativearray* a = new_nativearray_from (values, CLASSID_ARRAY_INT32, 
+            itemsize, numdims, itemcount, 0, 0);  
+
+        etch_int32* resultobj = remote->sum (remote, (etch_arraytype*) a);
+
+        if (0 == (result = perf_xmpl_validate_response_object ((objmask*)resultobj, 0)))
+            if  (is_etch_int32(resultobj))
+                 if  (resultobj->value == 36)
+                      perf_xmpl_log_resultokex(thistest);
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        ETCHOBJ_DESTROY(resultobj);
+    }
+
+    /* stop and dispose of remote server */
+    /* note that if we omit the stop_waitdown() here, behavior is unchanged */
+    result = remote->remote_base->stop_waitdown (remote->remote_base, p->waitms);
+    assert(0 == result);
+
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/* - - - - - - - - -  
+ * perf.report()
+ * - - - - - - - - -  
+ */
+
+/**
+ * perftest_threadproc_report
+ * perftest thread procedure for specific perf test "sum"
+ */
+int perftest_threadproc_report (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+    const int msgcode = 23;
+    wchar_t*  msgtext = L"this message describes the specifics of code 23";
+    char* thistest = "perf.report";
+
+    /* create parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  /* execute perf.report() n times */
+    {
+        etch_int32* resultobj = remote->report (remote, new_int32(msgcode), new_stringw(msgtext));   
+
+        if (0 == (result = perf_xmpl_validate_response_object ((objmask*)resultobj, 0)))
+            if  (is_etch_int32(resultobj))
+                 if  (resultobj->value == 0)
+                      perf_xmpl_log_resultokex(thistest);
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        ETCHOBJ_DESTROY(resultobj);
+    }       
+
+    /* dispose of server */
+    result = remote->remote_base->stop_waitdown(remote->remote_base, p->waitms);
+    assert(0 == result);
+
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/* - - - - - - - - -  
+ * perf.dist()
+ * - - - - - - - - -  
+ */
+
+/**
+ * perftest_threadproc_dist
+ * perftest thread procedure for specific perf test "dist"
+ */
+int perftest_threadproc_dist (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+    const int a1 = 1, a2 = 2, b1 = 1000000000, b2 = 2000000000;
+    char* thistest = "perf.dist";
+
+    /* seed parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  /* execute perf.dist() n times */
+    {
+        perf_point* resultobj = remote->dist (remote, new_perf_point(a1,a2), new_perf_point(b1,b2));
+
+        if (0 == (result = perf_xmpl_validate_response_object ((objmask*)resultobj, 0)))
+            if  (is_perf_point(resultobj))
+                 if  (resultobj->x && resultobj->y)  /* todo check actual expected values here */
+                      perf_xmpl_log_resultokex(thistest);
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        ETCHOBJ_DESTROY(resultobj);
+    }
+
+    /* dispose of server */
+    /* note that if we omit the stop_waitdown() here, behavior is unchanged */
+    result = remote->remote_base->stop_waitdown(remote->remote_base, p->waitms);
+    assert(0 == result);
+
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/* - - - - - - - - -  
+ * perf.add2()
+ * - - - - - - - - -  
+ */
+
+/**
+ * perftest_threadproc_add2
+ * perftest thread procedure for specific perf test "add2"
+ */
+int perftest_threadproc_add2 (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+    const int64 adjsecs = 365*24*60*60, adjms = adjsecs * 1000; /* 1 year */
+    char* thistest = "perf.add2";
+
+    /* seed parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  /* execute perf.dist() n times */
+    {   
+        etch_date* resultobj = remote->add2 (remote, new_date(), new_int64(adjms));
+
+        if (0 == (result = perf_xmpl_validate_response_object ((objmask*)resultobj, 0)))
+            if  (is_etch_date(resultobj))
+                 if  (resultobj->value)  /* todo check actual expected value here */
+                      perf_xmpl_log_resultokex(thistest);
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        ETCHOBJ_DESTROY(resultobj);
+    }
+
+    /* dispose of server */
+    result = remote->remote_base->stop_waitdown(remote->remote_base, p->waitms);
+    assert(0 == result);
+
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/* - - - - - - - - -  
+ * perf.report2()
+ * - - - - - - - - -  
+ */
+
+/**
+ * perftest_threadproc_report2
+ * perftest thread procedure for specific perf test "report2"
+ */
+int perftest_threadproc_report2 (etch_threadparams* tp)
+{
+    perftestobj* p = perftest_get_params(tp);
+    int result = 0, iterations = p->iterations;
+    const int msgcode = 23;
+    wchar_t*  msgtext = L"this message describes the specifics of code 23";
+    char* thistest = "perf.report2";
+
+    /* seed parameter bundle with callback to editable client constructor */
+    etch_client_factory* impl_factory = new_client_factory (NULL, NULL, p->new_client);
+
+    /* instantiate a remote server, which invokes client constructor */
+    perf_remote_server* remote = new_remote_server (p->uri, impl_factory);
+    assert(remote);
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    /* wait for server to come up */ 
+    result = remote->remote_base->start_waitup (remote->remote_base, p->waitms);
+    assert(0 == result);
+    
+    while(iterations--)  /* execute perf.dist() n times */
+    {
+        etch_int32* resultobj = remote->report2 (remote, 
+            new_date(), new_int32(msgcode), new_stringw(msgtext));
+
+        if (0 == (result = perf_xmpl_validate_response_object ((objmask*)resultobj, 0)))
+            if  (is_etch_int32(resultobj))
+                 if  (resultobj->value == 0)
+                      perf_xmpl_log_resultokex(thistest);
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        ETCHOBJ_DESTROY(resultobj);
+    }
+
+    /* dispose of server */
+    result = remote->remote_base->stop_waitdown(remote->remote_base, p->waitms);
+    assert(0 == result);
+
+    ETCHOBJ_DESTROY(remote);
+
+    return 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * thread runners: execute specified test threadproc on specified number of threads
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * perftest_run_unthreaded()
+ * general perftest main thread runner.
+ */
+void perftest_run_unthreaded (perftestobj* p, perftest_threadproc runner) 
+{
+    etch_threadparams threadparams; etch_init_threadparams(&threadparams);
+    threadparams.data = p;
+    etchlog(ETCHTEST, ETCHLOG_INFO, "RUNNING %s on MAIN THREAD\n", p->descr);
+
+    runner(&threadparams);  
+}
+
+
+/**
+ * perftest_run_threads()
+ * general perftest concurrent test runner.
+ * @param p the test data object, not owned here.
+ */
+void perftest_run_threads (perftestobj* p, perftest_threadproc runner) 
+{
+    int i = 0;
+    etch_thread* newthread = 0;
+    etch_threadpool* pool = new_threadpool(ETCH_THREADPOOLTYPE_FREE, p->threads); 
+    pool->is_free_data = FALSE; /* prevent thread from destroying the perftestobj */
+    etchlog(ETCHTEST, ETCHLOG_INFO, "RUNNING %s ON %d THREADS\n", p->descr, p->threads);
+
+    for(; i < p->threads; i++) /* launch the requested number of tests */
+    {   /* note that the pool threads are started immediately by default. 
+         * if we wanted to instead start them explicitly, we would have 
+         * set pool->is_manual_start true, above, and later called 
+         * newthread->start(newthread);
+         */
+        etchlog(ETCHTEST, ETCHLOG_INFO, "STARTING %s ON THREAD %d\n", p->descr, i);
+        newthread = pool->run (pool, runner, p);
+        assert(newthread);
+    }
+
+    /* wait for all active pool threads to exit, and finally free memory */
+    pool->destroy(pool);    
+}
+
+
+/**
+ * perftest_run_one()
+ * general perftest single test runner.
+ */
+double perftest_run_one (perftestobj* p, const int n, perftest_threadproc run)
+{
+    int64 t0 = etch_system_nanotime(), t1 = 0;
+    p->iterations = n;
+
+    if (p->threads <= 1)
+        perftest_run_unthreaded(p, run);
+    else
+        perftest_run_threads(p, run);
+
+    t1 = etch_system_nanotime();
+
+    return (t1 - t0) / (double) 1000000000;
+}  
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - 
+ * test runner: per-service-method test execution
+ * - - - - - - - - - - - - - - - - - - - - - - - - 
+ */
+
+/**
+ * run_perftest()
+ * combines java perftest constructor and java perftest.run() 
+ */
+int run_perftest (char* descr, wchar_t* uri, const int waitms, const int runtime, 
+    const int count, const int threads, perftest_threadproc testproc, 
+    new_client_funcptr new_perfclient)
+{
+    char* mask = NULL;
+    perftestobj perftest; 
+    etch_arraylist* list = NULL;
+    double time = 0.0, sum = 0.0, r = 0.0;
+    int  result = 0, k = 0, oldn = 0, n = 1, i = 0, times = 0;
+    memset (&perftest, 0, sizeof(perftestobj));
+    perftest.sig   = PERFTESTOBJ_SIGNATURE;
+    perftest.count = count;
+    perftest.descr = descr;
+    perftest.uri   = uri;
+    perftest.runtime = runtime;
+    perftest.threads = threads;
+    perftest.waitms  = waitms;
+    perftest.new_client = new_perfclient;
+
+    /* use this to temporarily test run_one */
+    /* perftest_run_one (&perftest, n, testproc); */
+
+    mask = "%s %d took %f -- trying %d to get >= 1 second\n";
+    while(time < 1.0)
+    {
+        if (time > 0.1)
+            etchlog(ETCHTEST, ETCHLOG_INFO, mask, descr, oldn, time, n);
+        oldn = n;
+        time = perftest_run_one (&perftest, n, testproc);
+        etchlog(ETCHTEST, ETCHLOG_INFO, "****** run_one time result is %f\n", time);
+        n *= 2;
+        if (++times > 10) // failsafe loop exit
+        {  etchlog(ETCHTEST, ETCHLOG_INFO, "exiting run loop after max 10 times\n");
+           break;
+        }
+    }
+    n = oldn;
+
+#if(0)
+    k = 2;
+    n = (int) ((k * n) / time);
+    mask = "%s %d took %f -- trying %d for %d seconds\n";
+    etchlog(ETCHTEST, ETCHLOG_INFO, mask, descr, oldn, time, n, k);
+    oldn = n;
+    time = perftest_run_one(&perftest, n, testproc);
+
+    k = 4;
+    n = (int) ((k * n) / time);
+    mask = "%s %d took %f -- trying %d for %d seconds\n";
+    etchlog(ETCHTEST, ETCHLOG_INFO, mask, descr, oldn, time, n, k);
+    oldn = n;
+    time = perftest_run_one(&perftest, n, testproc);
+
+    n = (int) ((runtime * n) / time);
+    mask = "%s %d took %f -- using %d for %d seconds\n";
+    etchlog(ETCHTEST, ETCHLOG_INFO, mask, descr, oldn, time, n, runtime);
+
+    list = new_arraylist(count, 0);
+    list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+
+    mask = "%s %d/%d\t%d\t%d\t%f\t%f\t%f\n";
+    for(i = 0; i < count; i++)
+    {
+        time = perftest_run_one(&perftest, n, testproc);
+        sum += (r = n / time);
+        arraylist_add(list, new_double(r));
+        etchlog(ETCHTEST, ETCHLOG_INFO, mask, descr, i, count, threads, 
+            n, time, r, r*threads);
+    }
+#endif
+
+    /* todo: calculate and display min, max, media, mean */
+    ETCHOBJ_DESTROY(list);
+    return 0;
+}
+
+
+/**
+ * perf_xmpl_verify_server()
+ * ensure server is working.  
+ * @return count of failed tests.
+ */
+int perf_xmpl_verify_server (perf_remote_server* remote)
+{ 
+    char* thistest = NULL;
+    int testresult = 0;
+
+    /* redirect session notifications to here in order to catch exceptions
+     * returned from one-way messages. */
+    my_override_client_session_notify (remote);
+
+    #if(1)  
+
+    do  /* perf.add() */
+    {   etch_int32* addresult;
+        perf_xmpl_log_startmsg (thistest = "perf.add 1");
+
+        /* execute the remote add() call 2 + 3 */
+        /* arguments objects are relinquished, result object is assumed */
+        addresult = remote->add (remote, new_int32(2), new_int32(3));  
+
+        if (0 == (testresult = perf_xmpl_validate_response_object ((objmask*)addresult, 0)))
+            if  (is_etch_int32(addresult))
+                 if  (addresult->value == 5)
+                      perf_xmpl_log_resultok(thistest, "2 + 3 = 5");
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(addresult);
+    } while(0);
+
+    #endif
+
+    #if(1)  
+
+    do  /* perf.add() negative test */
+    {   etch_int32* addresult;
+        perf_xmpl_log_startmsg (thistest = "perf.add 2");
+        /* this test sends a bad object type (float) to perf.add().
+         * the result of this test will differ depending on whether validation
+         * is enabled on output. if so, an illegal arg exception should be thrown
+         * at the client which user will see. if not, serialization will fail at
+         * the server due to the validation error, server will not return a
+         * perf.add result, and user should see a timeout exception.
+         */
+
+        /* execute the remote add() call with invalid object types */
+        /* arguments objects are relinquished, result object is assumed */
+        addresult = remote->add (remote, (etch_int32*) new_int32(2), (etch_int32*) new_float(3));
+
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)addresult, EXCEPTION_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(addresult);
+    } while(0);
+
+    #endif
+
+
+    #if(1) 
+
+    do  /* perf.add() negative test */
+    {   etch_int32* addresult;
+        perf_xmpl_log_startmsg (thistest = "perf.add 3");
+        /* this test sends a certain integer value to perf.add() which the server
+         * implementation is coded to interpret as bad data for which it should
+         * throw an exception. the exception should be serialized back across the
+         * wire and user should see the exception.        
+         */
+
+        /* execute the remote add() call with invalid object types */
+        /* arguments objects are relinquished, result object is assumed */
+        addresult = remote->add (remote, (etch_int32*) 
+            new_int32(FAKEBADDATA_INT32),(etch_int32*) new_int32(3));
+
+        if  (0 != (testresult = perf_xmpl_validate_response_object((objmask*)addresult, EXCEPTION_EXPECTED)))
+             perf_xmpl_log_resulterr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(addresult);
+    } while(0);
+
+    #endif
+
+
+    #if(1)  
+
+    do   /* perf.sum() nativearray */
+    {   const int ITEMSIZE = sizeof(int), NUMDIMS = 1, NUMITEMS = 3;
+        const int DIM0 = NUMITEMS, DIM1 = 0, DIM2 = 0;
+        int ival0 = 1, ival1 = 2, ival2 = 3;
+
+        etch_int32* sumresult;
+        etch_nativearray* myarray; 
+        perf_xmpl_log_startmsg (thistest = "perf.sum 1");
+
+        myarray = new_nativearray(CLASSID_ARRAY_INT32, sizeof(int), NUMDIMS, DIM0, DIM1, DIM2);
+        myarray->put1(myarray, &ival0, 0);
+        myarray->put1(myarray, &ival1, 1);
+        myarray->put1(myarray, &ival2, 2);
+
+        /* execute the remote sum() call */
+        /* arguments objects are relinquished, result object is assumed */
+        sumresult = remote->sum (remote, (etch_arraytype*) myarray);
+
+        if  (0 != (testresult = perf_xmpl_validate_response_object((objmask*)sumresult, 0)))
+            if  (is_etch_int32(sumresult))
+                 if  (sumresult->value == 6)
+                      perf_xmpl_log_resultok(thistest, "sum(1,2,3)=6");
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(sumresult);
+    } while(0);
+
+    #endif  
+
+
+    #if(1)  
+
+    do   /* perf.sum() arrayvalue */
+    {   etch_int32* sumresult;
+        etch_arrayvalue* myarray; 
+        perf_xmpl_log_startmsg (thistest = "perf.sum 2");
+
+        myarray = new_arrayvalue (ETCH_XTRNL_TYPECODE_INT, NULL, 1, 4, 4, FALSE, FALSE);
+        arrayvalue_add(myarray, (etch_object*) new_int32(1));
+        arrayvalue_add(myarray, (etch_object*) new_int32(2));
+        arrayvalue_add(myarray, (etch_object*) new_int32(3));
+
+        /* execute the remote sum() call */
+        /* arguments objects are relinquished, result object is assumed */
+        sumresult = remote->sum (remote, (etch_arraytype*) myarray);
+
+        if  (0 == (testresult = perf_xmpl_validate_response_object((objmask*)sumresult, 0)))
+            if  (is_etch_int32(sumresult))
+                 if  (sumresult->value == 6)
+                      perf_xmpl_log_resultok(thistest, "sum(1,2,3)=6");
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(sumresult);
+    } while(0);
+
+    #endif  
+
+
+    #if(1)  
+
+    do   /* perf.sum() negative - bad array object */
+    {   etch_int32 *sumresult, *not_an_array = new_int32(0);
+        perf_xmpl_log_startmsg (thistest = "perf.sum 3");
+
+        /* execute the remote sum() call  
+         * arguments objects are relinquished, result object is assumed  
+         * if validate on write is on, we expect a validation error at the client.
+         * if validate on write is off, we expect no result from server due to the
+         * resulting deserialization error, and a timeout exception at the client 
+         */
+        sumresult = remote->sum (remote, (etch_arraytype*) not_an_array);
+
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)sumresult, EXCEPTION_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+             
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(sumresult);
+    } while(0);
+
+    #endif  
+
+
+    #if(1)  
+
+    do   /* perf.sum() negative - bad array value */
+    {   const int ITEMSIZE = sizeof(int), NUMDIMS = 1, NUMITEMS = 3;
+        const int DIM0 = NUMITEMS, DIM1 = 0, DIM2 = 0;
+        int ival0 = 1, ival1 = FAKEBADDATA_INT32, ival2 = 3;  /* <=== BAD DATA */
+
+        etch_int32* sumresult;
+        etch_nativearray* myarray; 
+        perf_xmpl_log_startmsg (thistest = "perf.sum 4");
+
+        myarray = new_nativearray(CLASSID_ARRAY_INT32, sizeof(int), NUMDIMS, DIM0, DIM1, DIM2);
+        myarray->put1(myarray, &ival0, 0);
+        myarray->put1(myarray, &ival1, 1);
+        myarray->put1(myarray, &ival2, 2);
+
+        /* execute the remote sum() call  
+         * arguments objects are relinquished, result object is assumed  
+         * we expect to get an illegal argument exception  back from the server here.
+         */
+        sumresult = remote->sum (remote, (etch_arraytype*) myarray);
+
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)sumresult, EXCEPTION_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(sumresult);
+    } while(0);
+
+    #endif  
+
+
+    #if(1)  
+                                          
+    do  /* perf.report() */
+    {   objmask* represult = NULL;
+        perf_xmpl_log_startmsg (thistest = "perf.report 1");
+
+        /* execute the remote report() call.
+         * arguments objects are relinquished, result object is assumed.  
+         * this service method has no return type, however c binding always   
+         * returns an object shell in order to host a possible exception. 
+         */
+        represult  = remote->report (remote, new_int32(18), new_stringw(L"starting"));
+ 
+        testresult = perf_xmpl_validate_response_object ((objmask*)represult, 0);
+       
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(represult);
+
+    } while(0);
+
+    #endif
+
+     
+    #if(1)  
+                                          
+    do  /* perf.report() negative - bad parameter object */
+    {   objmask* represult = NULL;
+        perf_xmpl_log_startmsg (thistest = "perf.report 2");
+        my_reset_notifyinfo();
+
+        /* execute the remote report() call.
+         * arguments objects are relinquished, result object is assumed.  
+         * this service method has no return type, however c binding always   
+         * returns an object shell in order to host a possible exception. 
+         */                                 /* double is bad param here */
+        represult = remote->report (remote, (etch_int32*) new_double(0), new_stringw(L"it works!"));
+
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)represult, EXCEPTION_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+ 
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(represult);
+
+    } while(0);
+
+    #endif
+
+
+    #if(1)  
+                                          
+    do  /* perf.report() negative - bad data */
+    {   objmask* represult = NULL;
+        perf_xmpl_log_startmsg (thistest = "perf.report 3");
+        my_reset_notifyinfo();
+
+        /* execute the remote report() call.
+         * arguments objects are relinquished, result object is assumed.  
+         * this service method has no return type, however c binding always   
+         * returns an object shell in order to host a possible exception. 
+         */                                             /* FAKEBADDATA_STRING is bad data */
+        represult = remote->report (remote, new_int32(18), new_stringw(FAKEBADDATA_STRING));
+                               
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)represult, NOTIFY_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+       
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(represult);
+
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.dist() */
+    {   perf_point* distresult;
+        perf_xmpl_log_startmsg (thistest = "perf.dist 1");
+
+        /* execute the remote dist() call dist(point(1,2), point(3,5)) */
+        /* arguments objects are relinquished, result object is assumed */
+        distresult = remote->dist (remote, new_perf_point(1,2), new_perf_point(3,5));
+
+        if  (0 == (testresult = perf_xmpl_validate_response_object((objmask*)distresult, 0)))
+            if  (is_perf_point(distresult))
+                 if  (distresult->x == 2 && distresult->y == 3)
+                      perf_xmpl_log_resultok (thistest, "dist((1,2),(3,5)) = (2,3)");
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(distresult);
+    } while(0);
+
+    #endif
+
+
+   #if(1)
+
+    do  /* perf.dist() negative - bad parameter type */
+    {   perf_point* distresult;
+        perf_xmpl_log_startmsg (thistest = "perf.dist 2");     
+
+        /* execute the remote dist() call dist(point(1,2), point(3,5)) */
+        /* arguments objects are relinquished, result object is assumed */
+        distresult = remote->dist (remote, new_perf_point(1,2), (perf_point*) new_structvalue(NULL, 0));
+ 
+        if  (0 != (testresult = perf_xmpl_validate_response_object((objmask*)distresult, EXCEPTION_EXPECTED)))
+             perf_xmpl_log_resulterr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(distresult);
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.dist() negative - bad parameter value */
+    {   perf_point* distresult;
+        perf_xmpl_log_startmsg (thistest = "perf.dist 3");     
+
+        /* execute the remote dist() call dist(point(1,2), point(3,5)) */
+        /* arguments objects are relinquished, result object is assumed */  
+        distresult = remote->dist (remote, new_perf_point(1,2), new_perf_point(1, FAKEBADDATA_INT32));
+ 
+        if  (0 != (testresult = perf_xmpl_validate_response_object((objmask*)distresult, EXCEPTION_EXPECTED)))
+             perf_xmpl_log_resulterr(thistest);
+                    
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(distresult);
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.add2() */
+    {   etch_date* add2result;
+        const int64 adjms = 3600000;
+        etch_date*  now = new_date();
+        /* for this test, save date value for verify below, since date object relinquished */
+        const time_t oldtime = now->value; 
+        perf_xmpl_log_startmsg (thistest = "perf.add2 1");
+
+        /* execute the remote add2() call add2(date, msecs) */
+        /* arguments objects are relinquished, result object is assumed */
+        add2result = remote->add2(remote, now, new_int64(adjms));
+
+        if  (0 == (testresult = perf_xmpl_validate_response_object((objmask*)add2result, 0)))
+            if  (is_etch_date(add2result))
+                 if (add2result && !is_exception(add2result))
+                 {   const time_t newtime = add2result->value; /* time_t is in seconds */
+                     const int64  adjsecs = adjms / 1000;  /* so convert ms to seconds */
+                     const int64  chktime = newtime - adjsecs; 
+                     if  (chktime == oldtime) 
+                          perf_xmpl_log_resultok (thistest, "for add2(now, 3600000ms)");
+                     else perf_xmpl_log_resulterr(thistest);
+                 }
+                 else perf_xmpl_log_resulterr(thistest);
+            else perf_xmpl_log_objerr(thistest);
+
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(add2result);
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.add2() negative - bad parameter type */
+    {   etch_date* add2result;
+        perf_xmpl_log_startmsg (thistest = "perf.add2 2");
+
+        /* execute the remote add2() */
+        /* arguments objects are relinquished, result object is assumed */
+        add2result = remote->add2(remote, (etch_date*) new_double(0), new_int64(0));
+
+        if  (0 != (testresult = perf_xmpl_validate_response_object((objmask*)add2result, EXCEPTION_EXPECTED)))
+             perf_xmpl_log_resulterr(thistest);
+
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(add2result);
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.add2() negative - bad data value */
+    {   etch_date* add2result, *bad_date = new_date();
+        bad_date->value = FAKEBADDATA_DATE;
+        perf_xmpl_log_startmsg (thistest = "perf.add2 3");
+
+        /* execute the remote add2() */
+        /* arguments objects are relinquished, result object is assumed */
+        add2result = remote->add2(remote, bad_date, new_int64(0));
+
+        if  (0 != (testresult = perf_xmpl_validate_response_object((objmask*)add2result, EXCEPTION_EXPECTED)))
+             perf_xmpl_log_resulterr(thistest);
+
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(add2result);
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.report2() */
+    {   objmask* represult = NULL;
+        perf_xmpl_log_startmsg (thistest = "perf.report2 1");
+        my_reset_notifyinfo();
+
+        /* execute the remote report() call.
+         * arguments objects are relinquished, result object is assumed.  
+         * this service method has no return type, however c binding always   
+         * returns an object shell in order to host a possible exception. 
+         */
+        represult  = remote->report2 (remote, new_date(), new_int32(18), new_stringw(L"x"));
+ 
+        testresult = perf_xmpl_validate_response_object ((objmask*)represult, 0);
+       
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(represult);
+
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.report2() negative - bad parameter type */
+    {   objmask* represult = NULL;
+        perf_xmpl_log_startmsg (thistest = "perf.report2 2");
+        my_reset_notifyinfo();
+
+        /* execute the remote report() call.
+         * arguments objects are relinquished, result object is assumed.  
+         * this service method has no return type, however c binding always   
+         * returns an object shell in order to host a possible exception. 
+         */
+        represult = remote->report2 (remote, new_date(), new_int32(18), (etch_string*) new_int32(0));
+ 
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)represult, EXCEPTION_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+       
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(represult);
+
+    } while(0);
+
+    #endif
+
+
+    #if(1)
+
+    do  /* perf.report2() negative - bad data value */
+    {   objmask* represult = NULL;
+        perf_xmpl_log_startmsg (thistest = "perf.report2 3");
+        my_reset_notifyinfo();
+
+        /* execute the remote report() call.
+         * arguments objects are relinquished, result object is assumed.  
+         * this service method has no return type, however c binding always   
+         * returns an object shell in order to host a possible exception. 
+         */
+        represult = remote->report2 (remote, new_date(), new_int32(18), new_stringw(FAKEBADDATA_STRING));
+ 
+        if (0 != (testresult = perf_xmpl_validate_response_object((objmask*)represult, NOTIFY_EXPECTED)))
+            perf_xmpl_log_resulterr(thistest);
+       
+        perf_xmpl_log_endmsg (thistest, testresult);
+        ETCHOBJ_DESTROY(represult);
+
+    } while(0);
+
+    #endif
+
+
+    /* we sleep briefly here since if the last message was one-way, we will have
+     * not waited for a result, and if we now close the client socket before the 
+     * server has had a chance to run the message, the server would detect the 
+     * closed socket and shut down the client session, possibly prior to running
+     * the message implementation, and that test would therefore be incomplete.
+     * NOTE that if we are stepping though the server in the debugger, we should
+     * set config.sleepSecondsPriorClientExit to a sufficient number of seconds  
+     * such that the client socket will not close while doing so.
+     */
+    etch_sleep(500);
+    perf_xmpl_wait_seconds (config.sleepSecondsPriorClientExit);  
+
+    if (testerrors)
+        etchlog(ETCHTEST, ETCHLOG_ERROR, "perf_xmpl_verify_server %d PERF TESTS FAILED\n", testerrors);
+    else
+        etchlog(ETCHTEST, ETCHLOG_INFO, "perf_xmpl_verify_server ALL PERF TESTS PASS\n");
+
+    my_restore_client_session_notify (remote);
+    return testerrors;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - 
+ * call from main() to run all tests
+ * - - - - - - - - - - - - - - - - - - - - - 
+ */
+
+/**
+ * perf_xmpl_test_server()
+ * run tests  
+ */
+int perf_xmpl_test_server (wchar_t* uri, new_client_funcptr user_ctor, const int waitms)
+{  
+    const int runtime = 60, trials = 3, is_full = FALSE;
+    int i = 0, result = 0;
+    int threads[] = { 1, 2 };
+    /*  threads[] = { 1, 2, 3, 4, 5, 6, 7, 8, 16, 32 }; */
+    const int numthreadcounts = sizeof(threads) / sizeof(int);
+
+    /* since this test presumably runs after the standard generated code in [main],
+     * display any memory leaks now before starting the test, such that leaks from 
+     * the regular run can be isolated from any leaks occurring during this test, 
+     * the latter shown with the normal cumulative leak display at etch shutdown. 
+     */
+    // check_etch_heap_bytes (TRUE, TRUE);
+
+    etchlog(ETCHTEST, ETCHLOG_INFO, "start perf server test for %d iterations\n",
+            numthreadcounts);
+    
+    for(i = 0; i < numthreadcounts; i++)
+    {
+        const int numthreads = threads[i];
+        etchlog(ETCHTEST, ETCHLOG_INFO, "begin test iteration %d using %d threads ...\n",
+                i+1, numthreads);
+
+        #if(1)
+        etchlog (ETCHTEST, ETCHLOG_INFO, "run perf add test ...\n");
+        result = run_perftest ("add", uri, waitms, runtime, trials, numthreads, 
+            perftest_threadproc_add, user_ctor);
+
+        #endif
+
+        #if(0)
+        etchlog (ETCHTEST, ETCHLOG_INFO, "run perf sum test ...\n");
+        result = run_perftest ("sum", uri, waitms, runtime, trials, numthreads, 
+            perftest_threadproc_sum, user_ctor);
+        #endif
+
+        #if(0)
+        etchlog (ETCHTEST, ETCHLOG_INFO, "run perf report test ...\n");
+        result = run_perftest ("report", uri, waitms, runtime, trials, numthreads, 
+            perftest_threadproc_report, user_ctor);
+        #endif
+
+        #if(0)
+        etchlog (ETCHTEST, ETCHLOG_INFO, "run perf dist test ...\n");
+        result = run_perftest ("dist", uri, waitms, runtime, trials, numthreads, 
+            perftest_threadproc_dist, user_ctor);
+        #endif
+
+        #if(0)
+        etchlog (ETCHTEST, ETCHLOG_INFO, "run perf add2 test ...\n");
+        result = run_perftest ("add2", uri, waitms, runtime, trials, numthreads, 
+            perftest_threadproc_add2, user_ctor);
+        #endif
+
+        #if(0)
+        etchlog (ETCHTEST, ETCHLOG_INFO, "run perf report2 test ...\n");
+        result = run_perftest ("report2", uri, waitms, runtime, trials, numthreads, 
+            perftest_threadproc_report2, user_ctor);
+        #endif
+    }
+
+    etchlog (ETCHTEST, ETCHLOG_INFO, "end perf server test\n");
+    return 0;
+}
+

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,446 @@
+/* $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);
+}
+

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.h
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.h?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.h (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_helper.h Wed Apr 22 17:25:43 2009
@@ -0,0 +1,63 @@
+/* $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.h
+ * transport helper for perf service
+ */
+#ifndef PERF_HELPER_H
+#define PERF_HELPER_H
+
+#include "etch_apr.h"
+#include "etch_transport.h"
+#include "etch_stub.h"
+#include "xmpl_perf_client.h"
+#include "xmpl_perf_server.h"
+#include "xmpl_perf_client_stub.h"
+#include "xmpl_perf_server_stub.h"
+#include "xmpl_perf_remote_server.h"
+#include "xmpl_perf_remote_client.h"
+etch_runtime_exit_params exitparams;
+
+void perf_client_cleanup (perf_remote_server*);
+int  perf_runtime_init (const int is_client);
+int  perf_runtime_exit ();
+
+int  new_perf_resources (etch_server_factory*);
+
+/* indicate if compiling an etch example as opposed to a regular etch client */
+#define IS_ETCH_EXAMPLE TRUE 
+
+/* client side user calls */
+int etch_init();
+int etch_exit();
+perf_remote_server*  new_remote_server (wchar_t* uri, etch_client_factory*); 
+perf_remote_server*  start_perf_client (wchar_t* uri, new_client_funcptr, const int waitms);
+perf_client_impl*    get_perf_client_impl (perf_remote_server*);
+perf_client_stub*    get_perf_client_stub (perf_remote_server*);
+etch_stub*           get_perf_client_stubbase (perf_remote_server*);
+int stop_perf_client (perf_remote_server*, const int waitdownms);
+
+/* server side */
+void* new_helper_accepted_server (etch_server_factory*, etch_session*);  
+i_sessionlistener*  new_perf_listener (wchar_t* uri, main_new_server_funcptr);
+int run_perf_listener (i_sessionlistener*, const int waitupms);
+
+
+#endif /* PERF_HELPER_H */
+

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,66 @@
+/* $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_listener_main.c
+ * establishes client session listeners on the server.
+ */
+#include "xmpl_perf_listener_main.h"
+
+
+/**
+ * main()
+ * reference MainPerfListener.java. see serverexe.txt for callback map.    
+ */
+int _tmain(int argc, _TCHAR* argv[])
+{
+    int result = -1, is_waitkey = TRUE, waitupms = 4000;
+    wchar_t* uri = argc > 1? argv[1]: L"tcp://127.0.0.1:4004";     
+
+    if (0 == perf_runtime_init(INIT_ETCH_SERVER))  
+    {                 
+        i_sessionlistener* listener = new_perf_listener (uri, new_perf_server); 
+
+        result = run_perf_listener (listener, waitupms); /* start server and block */
+
+        /* at this point all connections are down and destroyed */
+        ETCHOBJ_DESTROY(listener);  /* destroy_etch_listener() */
+    }
+    
+    perf_runtime_exit ();
+    return waitkey (is_waitkey, result);
+}
+
+
+/**
+ * new_perf_server()
+ * create an individual client's perf server implementation.
+ * this is java binding's newPerfServer().
+ * this is called back from helper.new_helper_accepted_server() (java's newServer).  
+ * @param p parameter bundle. caller retains. 
+ * @return the i_perf_server, whose thisx is the perf_server_impl.
+ */
+i_perf_server* new_perf_server (etch_server_factory* p, etch_session* session)
+{
+    perf_remote_client* client  = (perf_remote_client*) session->client;
+
+    perf_server_impl* newserver = new_perf_server_impl (client);
+
+    return newserver->perf_server_base;
+}
+

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.h
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.h?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.h (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_listener_main.h Wed Apr 22 17:25:43 2009
@@ -0,0 +1,57 @@
+/* $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_listener_main.h
+ * server exe main() private header
+ */
+
+#ifndef ETCH_SERVERMAIN_H
+#define ETCH_SERVERMAIN_H
+
+#include "etch_apr.h"
+#include "etch_url.h"  
+#include "etchthread.h"
+#include "etch_global.h"
+#include "xmpl_perf_helper.h"
+#include "xmpl_perf_server_impl.h"
+#include "xmpl_perf_remote_client.h"
+#include <tchar.h>
+#include <stdio.h>
+
+#if(0)
+
+ IMPL_XXXX_SERVER
+ |  RemotePerfClient* client;
+ |  myfoo() { } etc; { /* service method implementations */ }
+ |  mydata;
+  - BASE_XXXX_SERVER
+    |  myfoo(); mybar(); etc. /* service method empty stubs */  
+    |- XXXX_SERVER: XXXX (interfaces)
+    |- myfoo(); mybar(); etc. /* service method interfaces */
+    |  mydata;
+     - OBJSESSION
+       |  _sessionControl(), _sessionNotify(), _sessionQuery()
+#endif
+
+
+i_perf_server* new_perf_server (etch_server_factory*, etch_session*);  
+
+
+#endif /* ETCH_SERVERMAIN_H */
+

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,269 @@
+/* $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_remote.c 
+ */
+
+#include "apr_time.h" /* some apr must be included first */
+#include "xmpl_perf_remote.h"
+#include "etch_global.h"
+#include "etch_url.h"  
+
+#include <tchar.h>
+#include <stdio.h>
+#include <conio.h>
+
+int destroy_perf_remote (perf_remote*);
+#if(0)
+etch_message* etchremote_new_message(perf_remote*, etch_type*);
+int etchremote_send(perf_remote*, etch_message*);
+int etchremote_begincall(perf_remote*, etch_message*, void**);
+int etchremote_endcall  (perf_remote*, i_mailbox*, etch_type*, void**);
+int etchremote_transport_control (perf_remote*, etch_event*, etch_int32*);
+int etchremote_transport_notify  (perf_remote*, etch_event*);
+objmask* etchremote_transport_query (perf_remote*, objmask*); 
+int etchremote_start_waitup  (perf_remote*, const int);
+int etchremote_stop_waitdown (perf_remote*, const int);
+#endif
+
+
+/* - - - - - - - - - - - - - -  
+ * constructors
+ * - - - - - - - - - - - - - -  
+ */
+
+/**
+ * new_perf_remote  
+ * @param ids delivery service -- caller retains
+ * @param vf perf value factory - caller retains 
+ * @param iperf optional perf service interface -- caller retains
+ */
+perf_remote* new_perf_remote (void* thisx, 
+    i_delivery_service* ids, etch_value_factory* vf, i_perf* iservice)
+{
+    perf_remote* remote = (perf_remote*) new_object (sizeof(perf_remote), 
+        ETCHTYPEB_REMOTE, get_dynamic_classid_unique(&CLASSID_REMOTE_PERF));
+
+    remote->destroy = destroy_perf_remote;
+
+    /* perf_remote instance data and methods */
+    remote->dsvc = ids; 
+    remote->vf   = vf;
+    remote->start_waitup  = etchremote_start_waitup;
+    remote->stop_waitdown = etchremote_stop_waitdown;
+
+    /* transport methods */
+    remote->transport_control = etchremote_transport_control;
+    remote->transport_notify  = etchremote_transport_notify;
+    remote->transport_query   = etchremote_transport_query;
+
+    /* remote base */
+    remote->new_message = etchremote_new_message;
+    remote->send        = etchremote_send;
+    remote->sendex      = etchremote_sendex;
+    remote->begin_call  = etchremote_begincall;
+    remote->end_call    = etchremote_endcall;
+
+    /* perf service */
+    if (iservice)
+        remote->iperf = iservice;
+    else
+    {   remote->iperf = new_perf_service_interface();
+        remote->is_service_interface_owned = TRUE;
+    }
+    remote->add    = remote->iperf->add;
+    remote->sum    = remote->iperf->sum;
+    remote->report = remote->iperf->report;
+    remote->dist   = remote->iperf->dist;
+    remote->add2   = remote->iperf->add2;
+    remote->report2= remote->iperf->report2;
+    remote->point  = remote->iperf->point;
+ 
+    return remote;
+}
+
+
+/**
+ * destroy_perf_remote()
+ * perf_remote destructor.
+ */
+int destroy_perf_remote (perf_remote* thisx)
+{
+    if (NULL == thisx) return -1;
+    if (thisx->refcount > 0 && --thisx->refcount > 0) return -1;  
+
+    if (!is_etchobj_static_content(thisx))
+    {    
+        if (thisx->is_service_interface_owned && thisx->iperf)
+            thisx->iperf->destroy(thisx->iperf);
+    }
+            
+    return destroy_objectex((objmask*)thisx);
+}
+
+
+/* - - - - - - - - - - - - - -  
+ * remote methods
+ * - - - - - - - - - - - - - -  
+ */
+
+/**
+ * etchremote_new_message()
+ * instantiates a message to be sent via this.send() or this.begin_call().
+ * @param thisx this remote object.
+ * @param message_type type of message, caller retains.
+ * @return message object, which could wrap an exception.
+ */
+#if(0)
+etch_message* etchremote_new_message (perf_remote* thisx, etch_type* message_type)
+{
+    etch_message* msg = new_message(message_type, ETCH_DEFSIZE, thisx->vf);
+    return msg;
+}
+#endif
+
+
+/**
+ * etchremote_send()
+ * sends message to recipient without waiting for a response.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_send (perf_remote* thisx, etch_message* msg)
+{
+    const int result = thisx->dsvc->itm->transport_message(thisx->dsvc, NULL, msg);
+    return result;
+}
+#endif
+
+
+/**
+ * etchremote_begincall()
+ * sends message beginning a call sequence.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return in out parameter, a mailbox which can be used to retrieve the response.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_begincall (perf_remote* thisx, etch_message* msg, i_mailbox** out)
+{
+    const int result = thisx->dsvc->begin_call(thisx->dsvc, msg, out);
+    return result;
+}
+#endif
+
+
+/**
+ * etchremote_endcall()
+ * finishes a call sequence by waiting for a response message.
+ * @param thisx this remote object.
+ * @param mbox a mailbox which will be used to read an expected message response.
+ * @param response_type the message type of the expected response.
+ * @return in out parameter, on success, the response.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_endcall (perf_remote* thisx, i_mailbox* mbox, etch_type* response_type, objmask** out)
+{
+    const int result = thisx->dsvc->end_call(thisx->dsvc, mbox, response_type, out);
+    return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_control()
+ * @param evt caller relinquishes
+ * @param value caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_transport_control (perf_remote* thisx, etch_event* evt, etch_int32* value)
+{
+    const int result = thisx->dsvc->itm->transport_control(thisx->dsvc, evt, value);
+    return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_notify()
+ * @param evt caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_transport_notify  (perf_remote* thisx, etch_event* evt)
+{
+    const int result = thisx->dsvc->itm->transport_notify(thisx->dsvc, evt);
+    return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_query()
+ * @param query caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+objmask* etchremote_transport_query (perf_remote* thisx, objmask* query) 
+{
+    objmask* resultobj = thisx->dsvc->itm->transport_query(thisx->dsvc, query);
+    return resultobj;
+}
+#endif
+
+
+/**
+ * etchremote_start_waitup()
+ * start the transport and wait for it to come up.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_start_waitup (perf_remote* thisx, const int waitms)
+{
+    const int result = thisx->transport_control(thisx, 
+        new_etch_event(CLASSID_CONTROL_START_WAITUP, waitms), NULL);
+
+    return result;
+}
+#endif
+
+
+/**
+ * etchremote_stop_waitdown()
+ * stop the transport and wait for it to go down.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_stop_waitdown (perf_remote* thisx, const int waitms)
+{
+    const int result = thisx->transport_control(thisx, 
+        new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, waitms), NULL);
+
+    return result;
+}
+#endif

Added: incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.h
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.h?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.h (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/xamples/perf/xmpl_perf_remote.h Wed Apr 22 17:25:43 2009
@@ -0,0 +1,105 @@
+/* $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_remote.h
+ * perf remote.
+ * combines java bindings's RemotePerf, Perf, and RemoteBase.
+ */
+
+#ifndef PERF_REMOTE_H
+#define PERF_REMOTE_H
+
+#include "xmpl_perf.h"
+#include "etch_remote.h"
+#include "etch_transport.h"
+unsigned short CLASSID_REMOTE_PERF;
+
+
+/**
+ * perf_remote
+ */
+typedef struct perf_remote
+{
+    unsigned int    hashkey;  
+    unsigned short  obj_type; 
+    unsigned short  class_id;
+    struct objmask* vtab;  
+    int  (*destroy)(void*);
+    void*(*clone)  (void*); 
+    obj_gethashkey  get_hashkey;
+    struct objmask* parent;
+    etchresult*     result;
+    unsigned int    refcount;
+    unsigned int    length;
+    unsigned char   is_null;
+    unsigned char   is_copy;
+    unsigned char   is_static;
+    unsigned char   reserved;
+
+    i_perf*  iperf;             /* possibly owned */
+    i_delivery_service*  dsvc;  /* not owned */
+    etch_value_factory*  vf;    /* not owned */
+    unsigned char  remote_type; /* client or server */
+    unsigned char  is_service_interface_owned;
+    unsigned short unused;      /* alignment */
+
+    etch_message* (*new_message) (void*, etch_type*);
+    int   (*send)   (void*, etch_message*);
+    void* (*sendex) (void*, etch_message*);
+    etch_delivsvc_begincall begin_call;  /* i_mailbox** out */
+    etch_delvisvc_endcall   end_call;    /* objmask** out */
+
+    int (*start_waitup)  (void*, const int waitms);
+    int (*stop_waitdown) (void*, const int waitms);
+
+    /* - - - - - - - - - - - - -
+     * transport functionality
+     * - - - - - - - - - - - - -
+     */
+    etch_transport_control transport_control;  
+    etch_transport_notify  transport_notify;   
+    etch_transport_query   transport_query; 
+    etch_transport_get_session get_session;  
+    etch_transport_set_session set_session; 
+
+    /* - - - - - - - - - - -
+     * service virtuals
+     * - - - - - - - - - - -
+     */
+    perf_add      add;
+    perf_sum      sum;
+    perf_report   report;
+    perf_dist     dist;
+    perf_add2     add2;
+    perf_report2  report2;
+    perf_point*   point;
+
+    /* - - - - - - - - - - -
+     * private instance data
+     * - - - - - - - - - - -
+     */    
+
+} perf_remote;
+
+
+perf_remote* new_perf_remote (void*, i_delivery_service*, etch_value_factory*, i_perf*);
+
+
+#endif /* PERF_REMOTE_H */
+