You are not logged in.
Pages: 1
Is it possible to automatically synronise preselected method with main thread?
It would be nice to do something like SynhronizedWithMainThread(['MyMethod1','MyMethod2',...]);
If not, is there a way to implement this somehow? Is there some central calling finction that could be
syncronized?
Thank you and all the best,
Vojko Cendak
Offline
In mORMot, by design, the service methods are completely decoupled from the UI, and should work in a thread-safe and stateless manner.
So, from the design POV, I do not know any reason of such synchronization: in mORMot, there is no such "main thread". Only multiple threads of the same priority, which are designed to serve as many client as possible.
For thread-safety, you can use critical sections or any other classic means of synchronization.
For your request, you can simply call the VCL Synchronize() method within the method service implementation.
Offline
sure I totally understand.
We deal a lot with COM server objects, which are all in "main" thread and if we need then we do it with Thread.Synchronize.
I believe, the scenario is basically the same as with Shared Object with critical sections, except here is Synchronize.
It would be "nice" if framework could do it behind the scenes. We had the same problem with RO (Remote Objects), but
there we hacked it with special simple invoke object, which was kind of general for our situation.
The .NET solution is Invoke(...) (I think)
Offline
You are right: I was not thinking about COM thread model!
In mORMot, for instance with OleDB, the way of using COM was to call CoInitialize() in each thread, then CoUninitialize() then the thread finishes.
The THttpServerGeneric.OnHttpThreadTerminate property is to be used for this task.
See http://synopse.info/fossil/tktview?name=213544b2f5 and http://synopse.info/forum/viewtopic.php?id=696
This can be a bit difficult to implement, so I will probably make some refactoring to make it easier to use.
Proper use of COM in each thread is IMHO much better than synchronize all access into the "main thread".
Offline
I've made some code refactoring to have the new virtual TSQLRestServer.EndCurrentThread method called from each terminating thread.
By default, every TSQLDBConnectionPropertiesThreadSafe instance will be called by SQLite3DB to release any thread-specific connection, e.g. after OleDB CoInitializae calls.
That is, it will fix any resource leak when using external database in an HTTP multi-thread server, for instance.
See http://synopse.info/fossil/info/ba6ff51ae9
This TSQLRestServer.EndCurrentThread method is the right place to release any thread-specific resource, e.g. COM objects in your case.
I've made CoInit and CoUInit public, if you need a wrapper to CoInitialize and CoUninitialize APIs.
See http://synopse.info/fossil/info/89098789e1
I'll look into the ability to call directly "Synchronize" around dedicated methods.
But using a dedicated protected method in the server implementation class sounds the better implementation for me.
And "Synchronize" has some known performance issue - it is always better to initialize the COM stack for each thread, AFAIK.
Offline
In our case we don't deal only with COM clients per thread, but one server instance (like OPC server) per mORMot server.
I believe, OleDB is actuall client and not server.
So if we want to use COM server instance we need to synchronize all calls to it (COM does that automatically)
from (internal) clients like mORMot or RO in same server application.
Last edited by VojkoCendak (2012-09-03 13:34:16)
Offline
I've added TServiceFactoryServer.ExecuteInMainThread() method, to force a method to be executed with RunningThread.Synchronize() call on multi-thread server instances (e.g. TSQLite3HttpServer or TSQLRestServerNamedPipeResponse).
/// force a method to be executed in the main thread
// - by default, service methods are called within the thread which received
// them, on multi-thread server instances (e.g. TSQLite3HttpServer or
// TSQLRestServerNamedPipeResponse), for better response time and CPU use -
// methods have to handle multi-threading safety carefully, e.g. by using
// TRTLCriticalSection mutex on purpose
// - ExecuteInMainThread() will force the method to be called within a
// RunningThread.Synchronize() call - it can be used e.g. if your
// implementation rely heavily on COM servers
// - methods names should be specified as an array (e.g. ['Add','Multiply'])
// - if Enable is left to TRUE default value, it will force main-thread
// execution; if set to FALSE, it will set it back to in-thread faster
// method execution
// - this method returns self in order to allow direct chaining of security
// calls, in a fluent interface
function ExecuteInMainThread(const aMethod: array of RawUTF8; Enable: boolean=true): TServiceFactoryServer;
It needed a lot of code refactoring, up to the TServiceMethod.InternalExecute low-level asm implementation, in order to be able to run Synchronize() in a generic way.
For instance, there is now a new TSQLRestServer.BeginCurrentThread virtual method, to implement such behavior.
I did not test it extensively, but I hope it is what you were looking for.
But do not expect very good performance with such methods, since the Synchronize() implementation is a bit slow, hardly relying on Windows semaphore/event API.
See http://synopse.info/fossil/info/f5d42ace61 and http://synopse.info/fossil/info/6600ed0b85
Offline
I've fixed the implementation and added multi-thread testing of ExecuteInMainThread() method.
See http://synopse.info/fossil/info/e98e24e118
Should be now working as expected.
In-process benchmarks states that calling Synchronize() is slower, but not to a big intent:
2.1. Service oriented architecture:
- Weak interfaces: 56 assertions passed 213us
- Service initialization: 127 assertions passed 3.46ms
- Direct call: 596,163 assertions passed 60.10ms
- Server side: 596,173 assertions passed 60.23ms
- Client side REST: 596,175 assertions passed 775.44ms
- Client side JSONRPC: 596,173 assertions passed 869.60ms
- Client side synchronized REST: 596,173 assertions passed 1.81s
- Security: 135 assertions passed 1.75ms
- Custom record layout: 596,173 assertions passed 881.01ms
Total failed: 0 / 3,577,348 - Service oriented architecture PASSED 4.47s
That is, 1.81s instead of 775.44ms.
This above benchmark is in-process, therefore on real Client-Server communication, I suspect there won't be any noticeable delay when using ExecuteInMainThread(), for the end-user.
Offline
Delphi2009 ent,
function TSQLRestServerDB.EngineRetrieveBlob(Table: TSQLRecordClass;
aID: integer; BlobField: PPropInfo; out BlobData: TSQLRawBlob): boolean;
var SQL: RawUTF8;
Req: PSQLRequest;
begin
result := false;
if (self=nil) or (DB=nil) or (aID<=0) or (Table=nil) or not BlobField^.IsBlob then <=== [DCC Error] SQLite3.pas(1014): E2008 Incompatible types (it's BlobFiled...)
exit;
and
function TSQLRestServerStaticExternal.EngineRetrieveBlob(
Table: TSQLRecordClass; aID: integer; BlobField: PPropInfo;
out BlobData: TSQLRawBlob): boolean;
var Rows: ISQLDBRows;
begin
result := false;
if (self=nil) or (Table<>fStoredClass) or (aID<=0) or not BlobField^.IsBlob then
exit;
thynk you,
Vojko
Last edited by VojkoCendak (2012-09-06 21:54:40)
Offline
It compiles fine with Delphi 6, 7, 2007, 2010 and XE2.
Delphi 2009 is just a buggy beast!
I tried to fix it with http://synopse.info/fossil/info/42f544fc40
Offline
I'm just informing yout that the code with mainthread synchronisation is working in production full time for several weeks.
Than you
Offline
Thanks for the feedback.
It is very gratifying to have such information from users.
I'm currently working with some C# project at the moment, and find out how WCF is on the same time very powerful (e.g. for its URI routing features with some attributes at interface level), but also much heavier and difficult to master / configure than our little mORMot.
But it gave me also some ideas and improvement path.
What is your feedback in comparison to DCOM or RO?
Offline
Yes it's working very good.
Just another possible improvement:
If I call with TSQLite3HttpClientWinSock from the main thread it gets stuck.
If I call it from anothen client app's main thread works ok. Also works ok on the server called form a thread.
Could you make a check whether it was called from the main thread and then not use the syncronise ?
Last edited by VojkoCendak (2012-11-14 20:52:20)
Offline
If I call with TSQLite3HttpClientWinSock from the main thread it gets stuck.
Does it mean that both TSQLite3HttpClientWinGeneric and TSQLite3HttpClientWinHTTP client classes work as expected?
What gets stuck? The client or the server side?
Offline
=== What gets stuck? The client or the server side?
ServerSide with a ServerSide implementation.
1. MainThread
2. TSQLite3HttpClientWinSock(Server).Services['mymainthreadservice'].Get(ImySvc)
3. call to ImySvc.mymethod ==> stuck here
SAME call from TThread.Execute --> OK;
Offline
- no, it was meant to show it is TSQLite3HttpClientWinSock class only
- means ...
sorry I should be more clear.
"same" call from thread works but NOT from mainthread. Maybe it's synhronization issue.
Last edited by VojkoCendak (2012-11-15 13:38:02)
Offline
=== What I still can't understand is that the synchronization take place at the server level only,
We use same client code serverside and on the client side (let's say some diagnostic forms, ....).
We don't want to use in the server like from TSQLRestServerDB.Create(FDBModel,MyModulePath+'\data\'+MyExeName+'.db3',False);) -> and call methods directly
and then on the client same call with TSQLite3HttpClientWinSock.Create(Host,IntToStr(Port),Model) -> call same methods from actual client.
=== so I do not find out why there is an issue depending on the client side...
So if we use TSQLite3HttpClientWinSock.Create(Host,IntToStr(Port),Model) SERVERSIDE and call it from main thread , the call is stuck, meaning
it doesn't get called on server, where as if the method is not syncronised with ExecuteInMainThread('..') it's ok.
I guess -> call (mainthr.) --> http--> dispatch call thread--> ... --> wants to synronise again with main thread and gets locked? I'm not sure.
We have this implementation we can use serverside and client side:
{$IFDEF SERVER} // code used on server
FDB := TSQLRestServerDB.Create(FDBModel,MyModulePath+'\data\'+MyExeName+'.db3',False);
{$ELSE} // code used on client ===> we want tu use this one also on the server
Server:= TSQLite3HttpClientWinSock.Create(Host,IntToStr(Port),Model);
{$ENDIF}
// actual implementation
{$IFDEF SERVER}
// call on server in same form
FDB.services['Potopne_Svc'].Get(CN);
{$ELSE}
// call on client
Server.Services['Potopne_Svc'].Get(CN)
{$ENDIF}
CN.mymethod
But it's kind of ugly and we have to maintain defines ...
Thank you for your time
Last edited by VojkoCendak (2012-11-16 07:14:14)
Offline
Yes if our client would run only on server .
I know it's faster to use direct connection on server and on client like TCPIP, HTTP.
We used to have serveral protocols: in server, clients on server computer and clients over network,
but it's much more complicated instead of using just one property like Host and that's it.
Speed optimization is done in other areas...
thank you
Last edited by VojkoCendak (2012-11-16 15:52:56)
Offline
Why not just use a TSQLRest kind of variable?
It will work with any kind of instance, either server or client, and whatever protocol will be implemented.
TSQLRest has all CRUD methods of the ORM, and also a Services property, so you can call directly services without adding any error-prone $IFDEF.
Just the initialization is to be handled as expected, then use is the same.
Offline
I think we went of track:
Usage serverside:
If the service method is synchronized and called within main thread then aClient's .Get(myIntf) -> myIntf.mymethod gets stuck. ==> here's error (serverside).
aClient is TSQLite3HttpClientWinSock for the time being.
It's beacuse of this error we are forced to use FDB (serverside) and with IFDEFS.
In RO server is just activated and that's it and then we use "clients" serverside and clientside normally.
2. also FDB stays create till the end but aClient:=TSQLite3HttpClientWinSock.Create(Host,IntToStr(Port),Model) can:
a. be instantained on demand
b. because of network problems, we must recreate aClient again and again
I wish my lack of undestranding of framework could be better.
IMHO documentation could be extended with precise and clear practical usage and at the end the detailed theory,
comparisons, blogs, documents, opinions. Help copied form code sucks or initial learning form source also. Last one is last resort.
Last edited by VojkoCendak (2012-11-17 01:44:56)
Offline
Pages: 1