On 3/23/23 15:29, Eric Blake wrote:
> On Thu, Mar 23, 2023 at 01:10:02PM +0100, Laszlo Ersek wrote:
>> The CONNECT_SA.START handler constructs several resources; some of those
>> are needed permanently if the entire handler succeeds, while others are
>> needed only temporarily, for constructing the permanent objects.
>> Currently, the various error branches in the handler deal with resource
>> release individually, leading to code duplication and dispersed
>> responsibilities; if we want to construct a new resource, then, under the
>> existent pattern, we'll have to modify multiple error branches to release
>> it as well.
>>
>> In my opinion, the best pattern for dealing with this scenario is the
>> following structure:
>>
>> - Place cascading error handling labels at the end of the function -- an
>>   error handling slide in effect. The order of destruction on this slide
>>   is the inverse of construction.
> 
> Yes, this style has gronw on me as I've seen it in more places.
> However,
> 
>> --- a/generator/states-connect-socket-activation.c
>> +++ b/generator/states-connect-socket-activation.c
>> @@ -97,132 +97,145 @@ prepare_socket_activation_environment (string_vector 
>> *env)
>>  
>>  STATE_MACHINE {
>>   CONNECT_SA.START:
>> +  enum state next;
>> +  char *tmpdir;
>> +  char *sockpath;
> 
> I've also come to appreciate __attribute__((cleanup)) handling for
> even less effort.  If we decide to cross-port that from nbdkit (which
> effectively limits libnbd to being build only by gcc and clang), we
> could mark these two variables as cleanup and initialized to NULL,
> then have even less code below because they get auto-freed on all
> error paths where they were set.
> 
> I'm _not_ asking you do to that in this series (no need to respin and
> fight even more rebase churn), so much as a question to Rich on
> whether we think limiting libnbd to the same platforms as where nbdkit
> currently compiles, instead of theoretically allowing libnbd to
> compile in a wider set of compilers (although we haven't actually
> tested that), is worth doing.
> 
>>  
>> -  /* Parent. */
>> -  close (s);
>> -  string_vector_empty (&env);
>> +  /* Parent -- we're done; commit. */
>> +  h->sact_tmpdir = tmpdir;
>> +  h->sact_sockpath = sockpath;
>>    h->pid = pid;
> 
> If we do a followup series to introduce cleanup attributes, the
> initial declaration of tmpdir and sockpath must be also initialized to
> NULL; then here, where we transfer the values to permanent storage, we
> must assign the temporaries back to NULL.  Going one step further,
> note that libvirt has copied ideas from the glib project of having
> macros that make it obvious when you are doing transfer semantics -
> changing ownership of a string from one variable to another by setting
> the original to NULL, all in one line of code.

Yes, "g_steal_pointer". (Either you or Dan has mentioned it recently.)

> 
>>  
>>    h->connaddrlen = sizeof addr;
>>    memcpy (&h->connaddr, &addr, h->connaddrlen);
>> -  SET_NEXT_STATE (%^CONNECT.START);
>> +  next = %^CONNECT.START;
>> +
>> +  /* fall through, for releasing the temporaries */
>> +
>> +empty_env:
>> +  string_vector_empty (&env);
>> +
>> +close_socket:
>> +  close (s);
>> +
>> +free_sockpath:
>> +  if (next == %.DEAD)
>> +    free (sockpath);
> 
> With transfer semantics, you can unconditionally call free (sockpath)
> here (without probing 'next') provided that 'sockpath' is initialized
> to NULL, and then re-set back to NULL upon the commit portion
> transferring the pointer to the permanent storage.  What's more, if
> you combine transfer semantics with attribute cleanup, the 'free
> (sockpath)' is automatic on all exit paths without needing this goto
> label or code.
> 
>> +
>> +rmdir_tmpdir:
>> +  if (next == %.DEAD)
>> +    rmdir (tmpdir);
> 
> With transfer semantics, this code would still have to be conditional,
> but the condition would change to 'if (tmpdir)' instead of depending
> on 'next'.
> 
>> +
>> +free_tmpdir:
>> +  if (next == %.DEAD)
>> +    free (tmpdir);
> 
> And with transfer semantics and attribute cleanup, this is another
> label we could elide.
> 
>> +
>> +done:
>> +  SET_NEXT_STATE (next);
>>    return 0;
>>  } /* END STATE MACHINE */
> 
> At any rate, while there are still more ways to rewrite this even more
> compactly (if we decide we want to pull in attribute cleanup), your
> refactor is fine for now.
> 
> Reviewed-by: Eric Blake <ebl...@redhat.com>
> 

Thanks!
_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to