You are not logged in.
Pages: 1
Hi Ab
I did not get time yet to study and memorize the entire manual, but here's the thing. I might be asking for something obvious.
I have an interface at server side, that is called from client side. (Nothing new there, works like a charm). This interface however produces some data that I want to store in (memory) tables so it can be adressed from client side using the obvious CreateFillAndPrepare methods etcetera.
The first question is: (How) Can I store these records on server side for availability in the correct Client session (memory tables you know). I seem to have no access to the correct REST instance on server side from within my interfaced object at server side... These objects are instantiated without any reference to the "owning" rest server...
Maybe adding an (optional) ISQLRestOwner interface for registered interfaces can fix this? The REST server would then only set the REST owner in case the object supports the SQLRestOwner interface.
(BTW except from the "root" server, my "workbase" rest servers are created dynamically for each "project database" opened).
Also: I dont like the idea of my objects being packed to JSON and unpacked from JSON just to store them, whuile the data is actually in the same process (server side).
So the second question is: How do I get around this?
Hans
Offline
About your 1st question, there is a global thread variable named ServiceContext to retrieve the running factory and its TSQLRest instance.
type
PServiceRunningContext = ^TServiceRunningContext;
/// will identify the currently running service on the server side
// - is the type of the global ServiceContext threadvar
TServiceRunningContext = record
/// the currently running service factory
// - it can be used within server-side implementation to retrieve the
// associated TSQLRestServer instance
Factory: TServiceFactoryServer;
/// the currently runnning session identifier which launched the method
// - make available the current session or authentication parameters
// (including e.g. user details via Factory.RestServer.SessionGetUser)
Session: ^TSQLRestServerSessionContext;
/// the thread which launched the request
// - is set by TSQLRestServer.BeginCurrentThread from multi-thread server
// handlers - e.g. TSQLite3HttpServer or TSQLRestServerNamedPipeResponse
RunningThread: TThread;
end;
threadvar
/// this thread-specific variable will be set with the currently running
// service context (on the server side)
// - is set by TServiceFactoryServer.ExecuteMethod() just before calling the
// implementation method of a service, allowing to retrieve the current
// execution context
// - its content is reset to zero out of the scope of a method execution
// - when used, a local copy or a PServiceRunningContext pointer should better
// be created, since accessing a threadvar has a non negligible performance cost
ServiceContext: TServiceRunningContext;
2. If you use in-memory optimized tables, i.e. TSQLRestServerStaticInMemory instance (see the doc), you can manage such very high-speed and low-memory storage.
You can have direct access to those, if needed, via e.g. the TSQLRestServer.GetStaticDataServer() method.
To avoid multi-threading error at writing, you can call TSQLRest.AcquireWrite() / ReleaseWrite() methods.
Offline
Thx - I already had an idea it was thought of.
Here's a little encapsulation tip...
In these kind of global variable constructs, I usually create a function to return (and optionally initialize) the variable. In this way I am sure "no-one" changes my global variable. (thus read-only)
I also came up with this nifty trick for (non thread) global variables because I ran into calling the initialization routine when it was actually shutting down
interface
function MyGlobalObject:TMyObject;
procedure DropMyGlobalObject;
implementation
VAR vMyGlobalObject:TMyObject=nil;
function MyGlobalObject:TMyObject;
begin
if NativeInt(vMyGlobalObject)=-1 then
raise EFinalizetionError.Create('Cannot initialize MyGlobalObject when shutting down') // the global object was finalized!
else if vMyGlobalObject=nil then
vMyGlobalObject=TMyObject.Create; // the global object needs to be initialized
Result:=vMyGlobalObject;
end;
procedure DropMyGlobalObject;
begin
if NativeInt(vMyGlobalObject)<>-1 then
FreeAndNil(vMyGlobalObject); // allows re-initialization. Comes in handy for unit tests.
end;
procedure FinalizeMyGlobalObject;
begin
DropMyGlobalObject;
NativeInt(vMyGlobalObject):=-1; // mark as finalized.
end;
initialization
finalization
FinalizeMyGlobalObject;
end.
Off course, the same trick goes for interfaces too. And as objects are always (at least!) on 4byte boundaries, the object pointer is impossible to be -1 for any real object instance, so... it;'s pretty safe,
One drawback is that there is no exception handling available during shutdown, so you may consider using a different solution than raising an exception. In the debugger, however, it works like a charm to find initialization/finalization culprits.
It would be nice to have this also for thread variables, but unfortunately there is no safe/watertight way I know of to "catch" terminating threads.
Last edited by h.hasenack (2012-11-05 20:31:23)
Offline
In fact, a threadvar kind of variable is implemented using an internal (inlined) function, and uses the OS abilities to have a per-thread storage.
After execution of the service method, it is initialized back to nil. So it is safe to be used.
Using explicitely a thread-driven approach allows several servers to be run at once in the same process, which may be the case in some uses.
Offline
Really?
I am pretty sure that an objkect/interface assigned to a threadvar will NOT be released automatically.
It it is set to nil, it's probably because you programmed it that way, as the compiler does not do this for you.
The encapsulation tip was mainly about making your global vars readonly.
The finalization tip was about the termination of the application to catch faulty initialization calls.
BTW I know about threadvars, they are simply thread-local but application global variables.
Offline
I wanted to say that the framework set it to nil after method execution.
There is a try..finally section to protect the threadvar content.
We may make the global vars readonly, but it may also use a pointer or a wrong value, in some cases.
Thanks for the input.
Offline
Thx
Offline
Pages: 1