Hi Alan and Jim
I entirely advocate this. This is the same model that libcurl uses
too. In libcurl the "context" variable is a typecast void* so is
entirely opaque to the user, but it gets cast to a structure
internally. I presume the idea would be that the user would use one
context per thread?

There are two other items highly related items that go hand in hand
with this and they are error reporting and removing the exit calls.
Actually this is where I feel C++ has an advantage over C, but things
can be done in C too. Basically when we hit an error that we cannot
deal with such as a memory allocation fail, we need to return all the
way back to the API entry point and somehow make an error code
available to the user. At the same time we need to make sure we don't
generate a segfault by accessing our failed allocation and we need to
free any memory along the way to avoid memory leaks. The naïve, error
prone and extremely labour intensive way to do this is to return error
codes from all our internal functions, make sure we check them and do
clean up at every stage. This will never ever work. The library is
just too complex.
C++ does this very well. We can use an array class for our memory
allocations which has a destructor that frees the memory. This means
that when an array goes out of scope the memory is automatically
freed. C++ also has an error reporting mechanism called exception
throwing. If we have code which can throw an exception then we put it
inside a special block called a try-catch block. If at any point no
matter how many layers down the function call stack, the code hits an
error that it cannot deal with it throws an exception using the
throw() function. At this point the code will return all the way back
to the try catch block and execute the code in the catch section.
Otherwise the catch code is ignored. The amazing thing about this is
that along the return path all scopes are exited "cleanly" causing the
destructors of all objects to be called and freeing all memory. This
is an incredibly clean way to do things.

However if we wish to stick with C, which is probably a valid thing to
do, then there are some similar things we can do. Instead of exception
throwing C has longjmp(), which does a similar thing. The main problem
is that we still need to free our memory. We can do this by creating a
plmalloc and plfree function which will keep track of what memory is
allocated/deallocated. If long_jmp gets called part of the error
handling that we will do is check if any memory is still allocated and
deallocate it. From my understanding this longjmp and memory pool
model is pretty standard in C error reporting.

Here is an example of how it would work:


void
c_plline( PLINT n, const PLFLT *x, const PLFLT *y, PLplotContext context )
{
    plstream *plsc = (plstream *)context;
    int val;
    val = setjmp( plsc->env ); //this returns 0, however if we call longjmp
                                             //using the same value
for env, we return
                                             //here (a bit like a goto
with a nonzero return value.

    if( val == 0 ) //run our code if we haven't had a longjmp call
    {
        if ( plsc->level < 3 )
        {
            longjmp( PLERR_INITLEVEL3, plsc->env );
        }
        plP_drawor_poly( x, y, n, plsc ); //somewhere in the depths
here we could call longjmp and
                                                           //we would
end up back at the setjmp call
    }

    //do cleanup, just in case we forgot to do it in the code and save
our error code and perhaps report
    //an error message
    plfreeall();
    reporterror( val, plsc ); //this could do any number of things
}

Things do get a little more complicated than this, for example if
internally we call an API function we need
to push the env variable onto a stack to avoid overwriting it and we
must avoid longjmping over C++ code otherwise we get undefined
behaviour.
There are some libraries that implement all this already, e.g.
https://github.com/guillermocalvo/exceptions4c


So in summary:

Pass in a context for helping thread safety - definitely
Decide how we wish to report an error, could be return val, callback
or a plgeterr( context ) call.

If we want to stick with C:
Create a memory pool for allocating memory and ensuring we avoid memory leaks
Use longjmp for reporting errors back to the API entry point, but bear
in mind issues with jumping over C++ code

If we would be interested in using C++:
use an array object for memory allocation and automatic freeing
use exceptions to report errors back to the API entry point
This would be much much much (I could add a lot of muches here) more
robust than doing things with C, but it does require a philosophical
change to how plplot is written.


My personal view is that the error reporting and removing exit calls
is actually more important than thread safety.

Note that even if we decide to use C++ internally we must maintain a C
interface. C++ at a library interface can be very bad.

Phil

On 23 February 2016 at 05:50, Jim Dishaw <j...@dishaw.org> wrote:
>
>> On Feb 22, 2016, at 5:48 PM, Alan W. Irwin <ir...@beluga.phys.uvic.ca> wrote:
>>
>> @Everybody: now on to my C idea for thread safety.
>>
>> My idea for implementing that (closely following what was done for the
>> C ephcom library case where David Howells implemented an ephcom
>> context to help provide an API that did not depend on static
>> variables) is to eliminate all static variables by using a
>> PLplotContext struct that contains all data that is currently stored
>> as static variables.
>>
>> Once that is implemented then the proper non-static way to use PLplot
>> would be to do something like the following:
>>
>> PLplotContext * context;
>>
>> context = CreatePLplotContext();
>> plparseopts(..., context);
>> plinit(context);
>> // other ordinary PLplot calls as usual but with
>> // context as last argument, e.g.,
>> plline(..., context);
>>
>> plend(context);
>>
>> where CreatePLplotContext would malloc a PLplotContext, and all the
>> PLplot API calls would refer to that extra context argument whenever
>> they needed access to any part of what were previously static variables
>> (e.g., such as plsc).
>>
>> The context-sensitive plend would do everything that plend currently does 
>> plus
>> destroy the context by freeing it.
>>
>> So far this follows pretty exactly what David Howells implemented for
>> ephcom.  Does everybody agree this general idea (a comprehensive API
>> change where a context argument was added to every function call) would
>> go a long way toward making PLplot thread safe?
>>
>
> I think that is a good approach.  I have done something similar in a mixed 
> C/Fortran environment and it appeared to work, though I did not rigorously 
> test the implementation.  The hard part will be chasing down all the static 
> allocations—they abound in the string handling.  I had a patch that I put 
> together that removed all the static char arrays used to format 
> information/error messages, but it never made it into the code.  I still have 
> the patch and can update and push it to repository now that i can commit.
>
>> We found in the ephcom case that both the Python and Fortran bindings
>> for ephcom could pass the C pointer to a context as arguments.  So we
>> could implement those bindings in a non-static way using the ephcom
>> equivalent of CreatePLplotContext above. But we retained the static
>> version of the API just in case some future binding of ephcom was for
>> a language that could not pass C pointer arguments, and we would also
>> want to retain the static API for similar reasons in the PLplot case.
>>
>> For static versions of plinit, plinit variants (e.g., plstar), and/or
>> all PLplot routines that can legitimately be called before plinit
>> would call CreatePLplotContext internally if the static variable
>> PLplotContextAddress was non-NULL and store that pointer in
>> PLplotContextAddress which would be referenced by every static PLplot
>> routine (but PLplotContextAddress would be completely ignored by the
>> non-static API).
>>
>
> I seem to recall there is a way to get per-thread initialization of variables 
> in C.  Is that the direction you want to go or do you want the context 
> address to be shared by all threads?
>
>> For backwards compatibility (e.g., to support those who don't care
>> about thread safety and who do not want to change their code) we would
>> want to retain the same name for the static API that are used
>> now.  But I would like to use the same names for the non-static cases
>> as well if that is possible (say by following the approach discussed
>> at <http://stackoverflow.com/questions/1472138/c-default-arguments>.)
>>
>> Further discussion is encouraged and welcome! Also, I am well aware I
>> have glossed over lots of details here.  That is because I frankly
>> don't completely understand all those details!  :-) Nevertheless,
>> assuming I have expressed the overview correctly of what would be
>> required, I hope someone will be inspired by that overview to go ahead
>> and implement the non-static C alternative as a very large step toward
>> the ability to use our library in a thread-safe way.
>>
>> Alan
>> __________________________
>> Alan W. Irwin
>>
>> Astronomical research affiliation with Department of Physics and Astronomy,
>> University of Victoria (astrowww.phys.uvic.ca).
>>
>> Programming affiliations with the FreeEOS equation-of-state
>> implementation for stellar interiors (freeeos.sf.net); the Time
>> Ephemerides project (timeephem.sf.net); PLplot scientific plotting
>> software package (plplot.sf.net); the libLASi project
>> (unifont.org/lasi); the Loads of Linux Links project (loll.sf.net);
>> and the Linux Brochure Project (lbproject.sf.net).
>> __________________________
>>
>> Linux-powered Science
>> __________________________
>>
>> ---------- Forwarded message ----------
>> Date: Tue, 10 Dec 2002 02:52:43 -0600 (CST)
>> From: Maurice LeBrun <m...@gazoo.ph.utexas.edu>
>> To: Alan W. Irwin <ir...@beluga.phys.uvic.ca>
>> Cc: PLplot development list <Plplot-devel@lists.sourceforge.net>
>> Subject: Re: [Plplot-devel] plinit, plend, plinit sequence now works,
>>     but I am having second thoughts
>>
>> I can't tell you the specific answer to your questions due to how maniacally
>> busy I am these days (having just joined Lightspeed Semiconductor), but I can
>> elucidate some of the plplot design ideas that have historically been
>> un-documented.  And, I can give it in an object-oriented context, which
>> (because it is "canonical") is a lot nicer than the "this is the way it 
>> should
>> work" approach I've used historically. :)
>>
>> This also includes proposals for change to how we do it now -- i.e. the
>> behavior of plinit().  I've always been somewhat bothered by the way stream 0
>> vs stream N is handled (this bugged me back in '94 but I wasn't exactly
>> swimming in free time then either).
>>
>> When plplot starts, you have the statically pre-allocated stream, stream 0.
>> Yes the stream 0 that I hate b/c it's not allocated on the heap like a proper
>> data-structure/object (in the plframe widget I automatically start from 
>> stream
>> 1).
>>
>> So stream 0 is like a class definition and an instance rolled into one.  What
>> I think we need to do is get rid of the "instance" part of this and leave
>> stream 0 as a "class definition" only.  In this case all the command line
>> arguments and initial pls...() calls (before plinit) serve to override the
>> default initializion of the class variables, i.e. set stream 0 parameters 
>> only
>>
>> In other words, stream 0 becomes the template for all plplot streams.  You 
>> can
>> use it, but once you've called plinit() you have your own "instance" of the
>> plplot "object" -- i.e. you have a new stream with all the relevant state 
>> info
>> copied from stream 0.  If you change it, it dies when your stream dies with
>> plend1().
>>
>> If you really want to change stream 0 (i.e. the "plplot object" "class data")
>> you can always set your stream number to 0 and fire away.
>>
>> To summarize:
>>
>> plinit() creates a new stream, copied from stream 0
>> plend1() deletes that stream
>> plend() deletes all streams, except of course stream 0 which is "class data"
>>
>> Let me know if any of this helps.
>>
>> --
>> Maurice LeBrun    m...@gazoo.ph.utexas.edu
>> Research Organization for Information Science and Technology of Japan (RIST)
>>
>> ------------------------------------------------------------------------------
>> Site24x7 APM Insight: Get Deep Visibility into Application Performance
>> APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
>> Monitor end-to-end web transactions and take corrective actions now
>> Troubleshoot faster and improve end-user experience. Signup Now!
>> http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
>> _______________________________________________
>> Plplot-devel mailing list
>> Plplot-devel@lists.sourceforge.net
>> https://lists.sourceforge.net/lists/listinfo/plplot-devel
>
>
> ------------------------------------------------------------------------------
> Site24x7 APM Insight: Get Deep Visibility into Application Performance
> APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
> Monitor end-to-end web transactions and take corrective actions now
> Troubleshoot faster and improve end-user experience. Signup Now!
> http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
> _______________________________________________
> Plplot-devel mailing list
> Plplot-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/plplot-devel

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
Plplot-devel mailing list
Plplot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/plplot-devel

Reply via email to