Re: [PATCH v3 19/27] refs: add a concept of a reference transaction

2014-04-15 Thread Junio C Hamano
Michael Haggerty mhag...@alum.mit.edu writes:

 In retrospect, you might have been objecting more to the misleading
 docstring than to the behavior as implemented at the time.  

Yeah, I was reacting to the comment that said create can delete ;-)

 The
 docstring implied that create could actually be used to delete a
 reference, but that was not true: it always checked that the reference
 didn't exist beforehand.  So at worst it could leave a non-existent
 reference un-created.

OK, all is clear then.  

  (2) keep the assert(), reject such user input at the callers, and
  document that these are invalid inputs;

 The current status in v3 is that (2) is implemented.

Good.  Thanks.
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 19/27] refs: add a concept of a reference transaction

2014-04-14 Thread Michael Haggerty
On 04/07/2014 09:13 PM, Junio C Hamano wrote:
 Michael Haggerty mhag...@alum.mit.edu writes:
 
 +void ref_transaction_create(struct ref_transaction *transaction,
 +const char *refname,
 +unsigned char *new_sha1,
 +int flags)
 +{
 +struct ref_update *update = add_update(transaction, refname);
 +
 +assert(!is_null_sha1(new_sha1));
 +hashcpy(update-new_sha1, new_sha1);
 +hashclr(update-old_sha1);
 +update-flags = flags;
 +update-have_old = 1;
 +}
 +
 +void ref_transaction_delete(struct ref_transaction *transaction,
 +const char *refname,
 +unsigned char *old_sha1,
 +int flags, int have_old)
 +{
 +struct ref_update *update = add_update(transaction, refname);
 +
 +update-flags = flags;
 +update-have_old = have_old;
 +if (have_old) {
 +assert(!is_null_sha1(old_sha1));
 +hashcpy(update-old_sha1, old_sha1);
 +}
 +}
 
 These assert()s will often turn into no-op in production builds.  If
 it is a bug in the code (i.e. the callers are responsible for
 catching these conditions and issuing errors, and there are actually
 such checks implemented in the callers), it is fine to have these as
 assert()s, but otherwise these should be 'if (...) die(BUG:)', I
 think.

I forgot to confirm that the callers *do* verify that they don't pass
incorrect values to ref_transaction_create() and
ref_transaction_delete().  But if they wouldn't, then die(BUG:) would
not be appropriate either, would it?  It would have to be die(you silly
user...).

Another reason I'm comfortable with only having assert()s in this case
is that even if the preconditions are not met, nothing really crazy
happens.  If I were guarding against something nastier, like a buffer
overflow, then I would more likely have used die(BUG:) instead.


It is not material to this discussion, but I wonder how often production
builds use NDEBUG.  I just checked that Debian does not; i.e.,
assertions are enabled for git there.  Personally I wouldn't run code
built with NDEBUG unless building for a severely resource-constrained
device, and even then I'd be pretty nervous about it.

Michael

-- 
Michael Haggerty
mhag...@alum.mit.edu
http://softwareswirl.blogspot.com/
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 19/27] refs: add a concept of a reference transaction

2014-04-14 Thread Junio C Hamano
Michael Haggerty mhag...@alum.mit.edu writes:

 I forgot to confirm that the callers *do* verify that they don't pass
 incorrect values to ref_transaction_create() and
 ref_transaction_delete().  But if they wouldn't, then die(BUG:) would
 not be appropriate either, would it?  It would have to be die(you silly
 user...).

Having the assert() there gives a confused message to the readers
(at least it did to this reader).

assert() implies that callers that are not buggy should not give
input that triggers the condition, which would mean it is the
callers' responsibility to sanity check the user-input to reject
creating a ref at 0{40} or deleting a ref whose old value is 0{40},
which in turn means these input are errors that need to be diagnosed
and documented.

But as you said below...

 ... even if the preconditions are not met, nothing really crazy
 happens.

I agree that it also is perfectly fine to treat such input as
not-an-input-error.

It is a signal that these checks are not 'if (...) die()' that the
code may take that stance.

I cannot tell which interpretation the code is trying to implement.

Any one of the following I can understand:

 (1) drop the assert(), declaring that the user input is perfectly
 fine;

 (2) keep the assert(), reject such user input at the callers, and
 document that these are invalid inputs;

 (3) replace the assert() with 'if (...) die(you silly user...)',
 and document that these are invalid inputs; or

 (4) same as (3) but replace with warn(), declaring such input as
 suspicious.

but the current assert() makes the code look cannot decide ;-).

I would consider the first two more sensible than the other two, and
am leaning slightly towards (1) over (2).

Thanks.
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 19/27] refs: add a concept of a reference transaction

2014-04-14 Thread Michael Haggerty
On 04/14/2014 11:25 PM, Junio C Hamano wrote:
 Michael Haggerty mhag...@alum.mit.edu writes:
 
 I forgot to confirm that the callers *do* verify that they don't pass
 incorrect values to ref_transaction_create() and
 ref_transaction_delete().  But if they wouldn't, then die(BUG:) would
 not be appropriate either, would it?  It would have to be die(you silly
 user...).
 
 Having the assert() there gives a confused message to the readers
 (at least it did to this reader).
 
 assert() implies that callers that are not buggy should not give
 input that triggers the condition, which would mean it is the
 callers' responsibility to sanity check the user-input to reject
 creating a ref at 0{40} or deleting a ref whose old value is 0{40},
 which in turn means these input are errors that need to be diagnosed
 and documented.

In v2 of this patch series, I didn't forbid calling create with new_sha
== 0{40}, because, even though it's not what the user would think of as
creating a reference, the code didn't care--it would just verify that
the reference didn't exist before and then leave it undefined.

Then in [1] you commented:

 Sounds a bit crazy that you can ask create, which verifies the
 absense of the thing, to delete a thing.

I reacted to your comment by changing the documentation to forbid
calling create with new_sha1 == 0{40}, and enforced the rule using an
assert().  At the same time I added an analogous restriction that if
delete is called with have_old set, then old_sha1 must not be 0{40}.

In retrospect, you might have been objecting more to the misleading
docstring than to the behavior as implemented at the time.  The
docstring implied that create could actually be used to delete a
reference, but that was not true: it always checked that the reference
didn't exist beforehand.  So at worst it could leave a non-existent
reference un-created.  Sorry for the confusion.

 But as you said below...
 
 ... even if the preconditions are not met, nothing really crazy
 happens.
 
 I agree that it also is perfectly fine to treat such input as
 not-an-input-error.

That was the case in v2.

 It is a signal that these checks are not 'if (...) die()' that the
 code may take that stance.
 
 I cannot tell which interpretation the code is trying to implement.
 
 Any one of the following I can understand:
 
  (1) drop the assert(), declaring that the user input is perfectly
  fine;
 
  (2) keep the assert(), reject such user input at the callers, and
  document that these are invalid inputs;
 
  (3) replace the assert() with 'if (...) die(you silly user...)',
  and document that these are invalid inputs; or
 
  (4) same as (3) but replace with warn(), declaring such input as
  suspicious.
 
 but the current assert() makes the code look cannot decide ;-).
 
 I would consider the first two more sensible than the other two, and
 am leaning slightly towards (1) over (2).

The current status in v3 is that (2) is implemented.

Obviously I don't feel strongly about it, given that I implemented (1)
in v2.  But I think that this restriction makes code at the calling site
easier to understand: create now always means create; i.e., if the
transaction goes through, then the reference exists afterwards.  And
delete always means delete; i.e., afterwards, there is one less
reference in the world.

Michael

[1] http://mid.gmane.org/xmqqtxaczvod@gitster.dls.corp.google.com

-- 
Michael Haggerty
mhag...@alum.mit.edu
http://softwareswirl.blogspot.com/
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 19/27] refs: add a concept of a reference transaction

2014-04-07 Thread Junio C Hamano
Michael Haggerty mhag...@alum.mit.edu writes:

 +void ref_transaction_create(struct ref_transaction *transaction,
 + const char *refname,
 + unsigned char *new_sha1,
 + int flags)
 +{
 + struct ref_update *update = add_update(transaction, refname);
 +
 + assert(!is_null_sha1(new_sha1));
 + hashcpy(update-new_sha1, new_sha1);
 + hashclr(update-old_sha1);
 + update-flags = flags;
 + update-have_old = 1;
 +}
 +
 +void ref_transaction_delete(struct ref_transaction *transaction,
 + const char *refname,
 + unsigned char *old_sha1,
 + int flags, int have_old)
 +{
 + struct ref_update *update = add_update(transaction, refname);
 +
 + update-flags = flags;
 + update-have_old = have_old;
 + if (have_old) {
 + assert(!is_null_sha1(old_sha1));
 + hashcpy(update-old_sha1, old_sha1);
 + }
 +}

These assert()s will often turn into no-op in production builds.  If
it is a bug in the code (i.e. the callers are responsible for
catching these conditions and issuing errors, and there are actually
such checks implemented in the callers), it is fine to have these as
assert()s, but otherwise these should be 'if (...) die(BUG:)', I
think.

Other than that, I did not spot anything questionable in this round.

Thanks; will replace the series (but on the same base as I needed to
apply the series there to compare what got changed with the old
version of corresponding change for each patches).
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html