On Fri, 5 Jan 2018 11:26:14 -0200 Gustavo Sverzut Barbieri <barbi...@gmail.com>
said:

> On Fri, Jan 5, 2018 at 11:04 AM, Carsten Haitzler <ras...@rasterman.com>
> wrote:
> > On Fri, 5 Jan 2018 10:53:46 -0200 Gustavo Sverzut Barbieri
> > <barbi...@gmail.com> said:
> >
> >> On Fri, Jan 5, 2018 at 4:04 AM, Carsten Haitzler <ras...@rasterman.com>
> >> wrote:
> >> > raster pushed a commit to branch master.
> >> >
> >> > http://git.enlightenment.org/core/efl.git/commit/?id=76b837002eaea56b5ecb174bffe284012084dc74
> >> >
> >> > commit 76b837002eaea56b5ecb174bffe284012084dc74
> >> > Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
> >> > Date:   Fri Jan 5 15:01:02 2018 +0900
> >> >
> >> >     efl loop - provide efl namespace versions of begin/end locks on
> >> > mainloop
> >> >
> >> >     add efl_main_loop_steal() and efl_main_loop_release() for new efl
> >> >     namespace versiosn of ecore_thread_main_loop_begin() and
> >> >     ecore_thread_main_loop_end().
> >>
> >> > +EAPI int
> >> > +efl_main_loop_steal(void)
> >> > +{
> >> > +   return ecore_thread_main_loop_begin();
> >> > +}
> >> > +
> >> > +EAPI int
> >> > +efl_main_loop_release(void)
> >> > +{
> >> > +   return ecore_thread_main_loop_end();
> >> > +}
> >>
> >> since we're targeting multi loop and all, this construct in the new
> >> API should be multi-loop as well, that is we should have:
> >
> > we can't do this multi-loop because this is also tied specifically to
> > switching eo domain id's from "thread" to "main" and back. main has one and
> > only one instance - the mainloop eo id. thread is a thread-local per thread
> > domain so you can't actually switch to another thread's domain ... except
> > the main loop.
> >
> > that's why this is like it is ... :) the "lack of being symmetrical for
> > loops" irked me but it's an necessary evil unfortunately.
> > instead for loop to loop i think i'd encourage "sync/async func call" api
> > stuff that's on the todo.
> 
> well, I can see the main loop blocking a worker thread to ask
> something to be done... if can't be done, then I'd try to avoid
> exposing this one.

yeah. can't be done.

but it is super-convenient to just wrap a blob of code with this steal/release
as opposed to having to put it into a special function somewhere else and then
use a callback. it makes it really easy for someone to fix "badly behaved
code". well that was the original reason to have these because apparently lots
of peolpe did write such bad code and just wrapping it in
ecore_thread_main_loop_begin/end() was a low-impact way of fixing that code.

admittedly eo now throws a spanner int he works of badly behaved code and
literally denies access to objects you are not clearly and cleanly meant to
access. the begin/end was a clean access mechanism (as was ecore_...
call_sync/async()). this was far easier. the begin/end style gives locks/clean
access to main loop objects but ALSO gives access to local thread data and
objects at the same time without having to package it up in a struct and pass
that to call_sync/async (for async you'd have to handle mallocing and freeing
all data in the struct too and the struct itself - or some kind of lifetime
management).

keep in mind the call_sync/async is on the list to implement. this, i think, is
the cleanest way to do this. i want to do this in a way where languages like
lua can be made to work with it (multiple lua states - one per thread), so
maybe a default of passing around "an array of strings" and then a direct
"optimized" way with void *'s for c/c++ with free funcs called for you when the
call is done?

i guess then the question is... is this still a good thing to have this
begin/end (steal/release) if we just have the above call_sync/async? as i said
- it is super convenient. given efl's ui lives in the main loop, this is really
handy to have to have some thread finally "implement ui changes" after it's
done some i/o or calculations or whatever.

> one thread pausing and working on another is also something that can
> happen in real life scenarios, particularly for poorly designed
> software that we know will happen.
> 
> the single issue to get this working is eoid isolation, right?
> fortunately there is time to improve that before the final release.

well technically ... i could add api to eo to find the eoid table for a
specific target thread and then specifically adopt that as the "main table".
right now with eo the main thread has a special place so it's findable by
everyone, but other threads live in a tls. it could be done with enough code
in eo to store table ptr's in a global structure alongside a thread handle and
code to look them up, and specifically adopt THOSE tables like the main loop
one can be adopted/pushed/switched to. but that code does not exist at the
moment. i am not sure if it should exist in the end at all? the ability to
adopt the main loop eo table was a necessary to make ecore_thread...begin/end
legacy stuff work still. 

> > see https://phab.enlightenment.org/T5522
> >
> > it has a lot more details in the todo now.
> >
> > btw i was wanting to talk about the whole reader/writer stuff using in
> > efl.net as i am going to have to have a similar io interface from parent to
> > child loop and back (and also between loops object siblings etc.)
> >
> > the whole buffered binbuf slice etc.
> 
> clarify :-) class name? method?

well read_changed() callbacks... i have to do:

  if (efl_io_reader_can_read_get(event->object)) {
    Eina_Slice slice = efl_io_buffered_stream_slice_get(event->object);
    eina_binbuf_append_slice(buf, slice); // store the data somewhere
  }

as opposed to just a simple:

  Efl_Data *dat = event->info;
  eina_binbuf_append(buf, dat->data, dat->size); // store the data somewhere

it's a lot more code to basically "deal with some new data that arrived".

> > thing just seems like overly complex
> > work/effort for just sending a buffer and getting a buffer of bytes to/from
> > a target object. :(
> >
> > what do you think?
> 
> efl.io.reader.read == read(2)
> efl.io.writer.write == write(2)
> efl.io.closer.close == close(2)
> 
> efl.io.reader.can_read == select(2) + readfds
> efl.io.writer.can_write == select(2) + writefds
> 
> they return up-to that amount of bytes, what's there to be read or
> what can be written without blocking. no buffering done, which makes
> everything simpler (the implementations are almost 1:1 on unix
> syscalls).
> 
> the buffering that was previously replicated in ecore_exe, ecore_con,
> ecore_ipc... is now moved to outside and can be reused everywhere...
> as it's based on the reader/writer interfaces. Then ecore_exe in eo
> will be much, much simpler... and code using ecore_exe in eo will
> looks very close to efl.net or other efl.io, allows to replace the
> source or destination with various objects, from different domains
> (network, file, memory, process...) and everything else will copy.

actually you hit my point. i wast the code to be very similar. be it efl.net
ipc stuff to/from a server or a client, or it's ipc to/from an exe or to/from a
thread... i'd like code to be re-usable with minimal or even almost zero
changes.

what i am also wondering is how we should have parsers built on top of this?
like xml/json/newline delimited strings/etc. etc. ... common data format
parsers... should they be an object that you register another object with as
long its of the class efl.io.reader ? or should it be buffered? or should you
inherit the base class and wrap it with a parser that eats the reader data and
produced "parsed data" in other events on the same object? (inherit efl.exe and
then do this?). multiple inheritance here could do the job... if the object
listens to its own events. the problem with the above design is you really can
have only a single reader for the raw read/buffered data because once read...
it's gone. this kind of disturbs me a bit given thinking about these parsers.

> see efl_io_copier_example.c, it's like a netcat-on-steroids...
> 
> if all you want is to have a buffered input/ouput, you wrap the
> unbuffered i/o with efl.io.buffered_stream, giving the unbuffered i/o
> as "inner_io". Then all your reads/writes will be buffered... this is
> similar to fdopen() + fwrite() and fread().

this is what i want... but only being able to have a single reader (eg for the
raw data) disturbs me (if we then want to also have parsers too). imagine
hooking up 2 parsers (via inheritance or via object linking/registering) ...
you can't. they are mutually exclusive. not sure why you'd want 2 parsers...
maybe one produces a superset of the other and is easier to deal with? e.g.
newline delimited text vs csv parser (which is a subset of newline ...) ?

> internally efl.io.buffered_stream keeps binbufs for reads and writes
> (as fread, fwrite)... you can consume them using efl.io.reader.read(),
> which will pop stuff from the read buffer... efl.io.writer.write()
> will push stuff to write buffer.
> 
> however, since many people want to "peek" at buffer, without really
> reading from it, you can get the read-only slice, example:
> 
>    - protocol: 4 bytes for integer length, followed by [length] bytes message
> 
> you can keep the message and check if the 4 bytes are there... and
> then peek if the rest of the message is there. If it's not, just do
> nothing... wait it to fill a bit more.

eh? what is this above? ... ? you mean add something like this?

> once it's all in there, you have 2 options:
> 
>  1) efl.io.reader.read() 4 bytes, then allocate the [length] array and
> efl.io.reader.read() that amount of message... this would drain the
> read binbuff;
> 
>  2) peek at data using efl.io.buffered_stream.slice.get(), create
> whatever you need out of the read-only memory, then discard it using
> efl.io.buffered_stream.discard()...
> 
> 
> whenever you use 1 or 2 is depending on your implementation, if you
> need to parse the memory slice and create something else, then likely
> #2 is more efficient and easier to use, requires no further
> allocation.
> 
> 
> 
> 
> >> efl_loop_steal(loop);
> >> efl_loop_release(loop);
> >>
> >> btw, these names are confusing... I like the old ecore version better,
> >> something like:
> >
> > well begin i thought i'd steer clear from because it gets confused with
> > ecore_main_loop_begin() so easily... :( this realyl does steal the mainloop
> > (steals a lock then releases it as well as stealing the eoid context).
> >
> >> efl_loop_thread_usage_begin(loop);
> >> efl_loop_thread_usage_end(loop);
> >>
> >> or my preference:
> >>
> >> efl_loop_thread_pause(loop);
> >> efl_loop_thread_resume(loop);
> >
> > indeed it pauses and resumes... but it ALSO "steals" the eoid context from
> > the mainloop so your code after the steal acts as if it were in the
> > mainloop eoid context (it can access mainloop eo objects now).
> 
> except you and few other core devs, nobody else knows what the hell
> eoid is, how it works, that's per thread...
> 
> and they really shouldn't have to know :-)

well they SHOULD know the following:

1. by default eo object references are thread-local. trying to access them
across thread boundaries is illegal and eo will likely catch these and complain
about them. it may not always catch 100% and you may accidentally mess with the
wrong object. eo object references are indirect and go through checks and
validation before use.

advanced knowledge:

2. there is a class of eo object (shared) that eo implements all the locking on
for you. all object calls including ref/unref, add/del, all methods,
property sets/gets, event calls etc. are locked for you so you don't have to
worry. they are globally accessible across all threads, but at a higher cost. at
least the current implementation uses a global lock so the idea is any shared
object is a fast "get in and out" thing. nothing like efl_loop_begin() which is
kind of a method that enters and "never" leaves. everything should be a fast
get/set or do x and quickly go in and out. this implementation might be able to
be improved, but as no one even knows about shared objects at the moment...
it's not high on the priority list, and frankly can be done later as a
relaxation of performance constraints.

#2 exists and works right now ... but it's unclear how this might be exposed in
all languages. in c you push/pop an eoid domain when creating a shared object
(push the shared domain, create it, then pop domain - we could have
efl_shared_add ... but at this point i wouldn't want to add more constructors).

-- 
------------- Codito, ergo sum - "I code, therefore I am" --------------
Carsten Haitzler - ras...@rasterman.com


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to