I forgot one more thing, and also was asked on IRC for some clarifications about another, so here I am, replying to myself.
A comment I forgot to make, which is to something cedric mentioned, and is something that I've said a million times already and I'll say it again because it's so damn important and people should stop doing it. Please do not delete objects implicitly. :( Connection is closed? Don't delete the object, I may steal be dealing with it. Let me eo_unref/del it if I'm still the rightful owner of the object, it's my responsibility. Maybe I still want to introspect it before it's gone? It's my decision to make... If you really think this pattern of autodeletion is useful, add a helper function people can add as a callback that just eo_dels an object. It's that simple. As for the clarification, I was asked to explain what I meant by "disagree with the concept". What I meant by that, is a "fatter" wrapper than thin. I just want a very thin wrapper, and I disagree with anything that's fatter. :) -- Tom On 04/08/16 17:24, Tom Hacohen wrote: > Hey Gustavo, > > I am going to give a few comments, though it's really hard to review > like this. Although we are creating API here, we don't really care about > the API, we care about the usage. Which means, I don't care about how > the .eo file looks like, I care about how the .c file that uses it looks > like. You haven't provided that, so it means I have to guess, > misunderstand and work extra hard only to start a discussion that will > include more of the above. I don't really have much experience/knowledge > about this topic, but to me, for efficiency and clarity, it seems like > the best way to approach it would be to have a very simple wrapper > around read()/write() that handles address resolution, timeout, lifetime > and loop integration is enough. I don't think we should care about > binbuf or any other complicated setups, all we should get is a const > string (which is essentially a pointer to the static buffer with which > we called "read"), and then decide what we want to do with it on the > client side. Calling binbuf_append is not much work. > All of this should be with simple eo callbacks. As you pointed out, this > is really a solved problem, I think most (all?) the frameworks and > languages do the basics quite the same. I'd follow suit, because I've > never had a bad experience with any. Keep the basics simple, and then > wrap as needed with a subclass (like for example, the send file cedric > suggested). > > 1. We no longer have a "constructor" just properties we mark as > constructing properties and sometimes are only allowed to set on > creation. In this case, since the real finalization is actually done in > "connect", we don't even need those, and can just have normal code there > that checks for those. > 2. I'd also opt for strings for addresses rather than enum/types and > etc. Feels more correct. > 3. I'd also avoid promises. > 4. As said, I'd keep it simple, so would avoid binbuf, and your blob > suggestion. > 5. There are a few naming issues, some have been pointed out. > > > I can probably find additional specific comments, though to be honest, > as said above, I mostly disagree with the concept, and can't really > comment without seeing user code, rather than API description. > > -- > Tom. > > On 03/08/16 05:41, Gustavo Sverzut Barbieri wrote: >> v2 with some fixes. >> >> The analysis of competitors at the end was omitted and you can find >> the updated GIST (diff/highlight): >> >> https://gist.github.com/barbieri/5bdf6c49b207543d764435d2494361ba >> >> Fixed: >> - changed base class from Eo.Base to Efl.Loop_User >> - Returns instead of @out parameters >> - added "drain" event to BaseSocket >> - fixed naming: local_address -> address_local, same for remote >> - added send_buffer_usage property to BaseSocket >> - added receive_buffer_size and send_buffer_size to BaseSocket >> - added address_connected to Socket (client) >> - added address and client_limit to Server >> - addresses of a socket contain port, thus SocketAddress not just Address >> type. >> >> Pending: >> - Address as strings? >> - Use promise<> for send? >> - Constructors without parameters, use properties and finalize? >> - How to have properties to be usable only in constructor->finalize >> timeline? >> - Drop blob and use binbuf? >> >> >> >> /* >> >> This is a draft Eo model to replace ecore_con_{server,client}. >> >> It will address the same usage, but with a new API that is closer >> to what other projects do, this will allow easier transition to >> EFL, without loosing any feature. >> >> At the of this file you can see a detailed analysis of Ecore_Con.h and >> competitors such as node.js, golang, qt and soletta. >> >> NOTE: SSL and Proxy are left out from the initial discussion, Qt is >> the only one to provide API for proxy and they are unused in our >> repository. SSL is usually handled via a subclass, not an upgrade from >> the existing connection. >> >> NOTE: this Eo is not valid or fully complete, it's used as a starting >> base. >> */ >> >> import eina_types; >> >> struct Efl.Net.Address { >> family: Efl.Net.Family; >> address: union { in, in6, bt... }; >> } >> >> struct Efl.Net.SocketAddress { >> address: Efl.Net.Address; >> port: uint16; >> } >> >> // TODO: string->Efl.Net.Address resolver. >> >> /* see http://solettaproject.github.io/docs/c-api/structsol__blob.html >> * >> * The idea is to allow an immutable memory to be reference counted and have >> * parent. >> * >> * Children reference their parent and releases the referece when they >> * die, this allows "windowing" or "slicing" of memory to be handled >> * easily, such as a subsurface keeping the parent framebuffer alive, >> * or a "line" keeping the backing mmap'ed file. >> * >> * Free function allows custom release, such as unloading an image or >> * munmap of a text file. >> * >> * This plays well with bindings, keep the object alive (inc ref) and >> * once the blob dies release them (dec ref). >> */ >> struct Eina.Blob { >> mem: void_ptr; >> parent: Eina.Blob; >> size: size; >> refcount: int; >> free: Eina_Blob_Free_Cb; >> } >> >> /* >> * Using 'Net' instead of 'Network' since all others use a shorter >> * prefix. Also allows to have a side-by-side implementation without >> * the need to immediately convert the all users and allows changing >> * paradigm of the new class -- of course legacy wrappers will be kept >> * for old users. >> */ >> abstract Efl.Net.BaseSocket (Efl.Loop_User) { >> [[Abstract class representing a network connection socket]] >> >> events { >> received @hot: Eina.Binbuf; [[data is available to read, >> if consumed use eina_binbuf_remove().]] >> sent: Eina.Blob, error; [[data specified by blob was sent, >> if error != 0, contains errno]] >> >> drain: void; [[all pending data was sent.]] >> error: int; [[errno]] >> } >> >> methods { >> send @virtual_pure { >> [[Queue data to be sent to remote]] >> params { >> @in data: Eina.Blob; [[data to queue for sending]] >> } >> >> return: int; [[0 on success, ENOSPC if no space left]] >> >> /* can provide a helper that sends memory, creating the >> blob internally, such as static-const (no free) or >> allocated memory (call free) >> */ >> >> // TODO: promise<size, Eina_Error>? >> } >> >> flush @virtual_pure { >> [[Try to send as much as data without blocking]] >> /* NOTE: changes behavior from current flush() that >> * really blocks! >> */ >> return: bool; [[true if all data was sent]] >> } >> >> @property address_local { >> [[the local IP or unix-local address. Analogous to >> getsockname()]] >> get @virtual_pure { >> } >> values { >> address: Efl.Net.SocketAddress; // TODO: string? >> } >> } >> >> @property address_remote { >> [[the remote IP or unix-local address. Analogous to >> getpeername()]] >> get @virtual_pure { >> } >> values { >> address: Efl.Net.SocketAddress; // TODO: string? >> } >> } >> >> /* TO BE REMOVED: >> * it seems the usage is to steal the fd to do something >> * else or set some flags. If so we could expose some >> * primitives such as fcntl_add(type, flags) and >> * fcntl_del(type, flags) as well as a destructor that >> * will return the stolen fd (destroys the object and >> * keeps fd alive). >> */ >> /* @property internal_fd { ... }*/ >> >> @property connected { >> [[if the socket is still connected.]] >> get @virtual_pure { >> } >> values { >> connected: bool; >> } >> } >> >> @property send_buffer_usage { /* maybe pending_send_bytes? */ >> [[how many bytes are queued to be sent. >> See property send_buffer_size.]] >> get @virtual_pure { >> } >> values { >> bytes: size; >> } >> } >> >> @property receive_buffer_size { >> [[Amount of bytes to use when receiving data. >> 0 is unlimited, > 0 is fixed size.]] >> get @virtual_pure { >> } >> values { >> bytes: size; >> } >> } >> >> @property send_buffer_size { >> [[Amount of bytes to use when sending data. >> 0 is unlimited, >> > 0 is an upper limit of queued bytes before >> send() returns ENOSPC]] >> get @virtual_pure { >> } >> values { >> bytes: size; >> } >> } >> >> @property timeout { /* this usually doesn't change, >> should be constructor-only */ >> [[timeout to close the connection.]] >> values { >> timeout: double; >> } >> } >> } >> } >> >> >> class Efl.Net.Socket (Efl.Net.BaseSocket) { >> [[This is a client socket that is created and connected to a >> specific server given to the constructor]] >> >> /* TODO: if address changes to string, add: resolved */ >> /* events { */ >> /* resolved: Efl.Net.Address; [[the connecting address was >> resolved]] */ >> /* } */ >> >> methods { >> connect { >> [[The constructor that connects the socket to a given server]] >> params { >> @in type: Efl.Net.Socket.Type; >> @in address: Efl.Net.SocketAddress; // TODO: string? >> @in timeout: double; >> } >> } >> >> @property address_connected { >> [[The address used to create and connect this socket. It >> is the unresolved address, see the property address_remote >> for the actual resolved address]] >> get @virtual_pure { >> } >> values { >> address: Efl.Net.SocketAddress; // TODO: string? >> } >> } >> } >> >> constructors { >> .connect; >> } >> } >> >> class Efl.Net.Server (Efl.Loop_User) { >> events { >> connection @hot: Efl.Net.BaseSocket; [[new connection was accepted]] >> error: int; [[errno]] >> /* no disconnect, listen on each individual connection */ >> } >> >> methods { >> constructor { >> params { >> /* these could be converted into a struct to scale >> * gracefully and allow just some members to be set, >> * like only timeout without setting client_limit or >> * reject_excess_clients. >> */ >> @in type: Efl.Net.Socket.Type; >> @in address: Efl.Net.SocketAddress; // TODO: string? >> @in client_limit: int; >> @in reject_excess_clients: bool; >> @in timeout: double; >> @in bind_flags: Efl.Net.Socket.Bind.Flags; /* reuse addr..*/ >> @in accept_flags: Efl.Net.Socket.Accept.Flags; /* just >> SOCK_CLOEXEC */ >> } >> } >> >> @property address { >> [[the local IP or unix-local address used to create and listen.]] >> get @virtual_pure { >> } >> values { >> address: Efl.Net.SocketAddress; // TODO: string? >> } >> } >> >> @property client_limit { >> [[number of concurrent clients accepted by this server]] >> get @virtual_pure { >> } >> values { >> client_limit: int; >> reject_excess_clients: bool; >> } >> } >> >> @property timeout { /* this usually doesn't change, >> should be constructor-only */ >> [[timeout to close the connection.]] >> values { >> timeout: double; >> } >> } >> } >> >> constructors { >> .constructor; >> } >> } >> >> > > > ------------------------------------------------------------------------------ > _______________________________________________ > enlightenment-devel mailing list > enlightenment-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/enlightenment-devel > ------------------------------------------------------------------------------ _______________________________________________ enlightenment-devel mailing list enlightenment-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-devel