qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC PATCH 0/5] Scoped locks using attribute((cleanup))


From: Eric Blake
Subject: Re: [Qemu-devel] [RFC PATCH 0/5] Scoped locks using attribute((cleanup))
Date: Mon, 11 Dec 2017 08:11:44 -0600
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.4.0

On 12/11/2017 03:38 AM, Peter Maydell wrote:
> On 8 December 2017 at 19:40, Eric Blake <address@hidden> wrote:
>> On 12/08/2017 04:55 AM, Paolo Bonzini wrote:
>>> Likewise,
>>>
>>>     QEMU_WITH_LOCK(QemuMutex, guard_name, &some_mutex) {
>>>         ...
>>>     }
>>>
>>> is the same as
>>>
>>>     qemu_mutex_lock(&some_mutex);
>>>     ...
>>>     qemu_mutex_unlock(&some_mutex);
>>>
>>> except that any returns within the region will unlock the mutex.
>>
>> Not just returns, but ANY manner that you leave the scope, including a
>> goto or just falling out of the end of the scope.
> 
> How about longjmp()ing out of it?

Easy to test:

==========
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>

void my_cleanup (int *ptr) {
  int *i = ptr;
  printf("in my_cleanup(%d)\n", *i);
}

jmp_buf jmp;

void foo(int i) {
  while (1) {
    __attribute__((cleanup(my_cleanup))) int j = i;
    if (i == 0) {
      printf("before leaving scope by return\n");
      return;
    }
    if (i == 1) {
      goto label;
    }
    if (i == 3) {
      longjmp(jmp, 1);
    }
    if (i == 4) {
      printf("before leaving scope by exit\n");
      exit(0);
    }
    break;
  }
  printf("after leaving scope by break\n");
  return;
 label:
  printf("after leaving scope by return\n");
}

int main(void) {
  foo(0);
  foo(1);
  foo(2);
  if (!setjmp(jmp)) {
    foo(3);
  }
  printf("after leaving scope by longjmp\n");
  foo(4);
}
============

But the results aren't necessarily nice, depending on how we currently
(ab)use longjmp. Under Fedora 27
gcc (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)

I get

$ ./foo
before leaving scope by return
in my_cleanup(0)
in my_cleanup(1)
after leaving scope by return
in my_cleanup(2)
after leaving scope by break
after leaving scope by longjmp
before leaving scope by exit

I don't know if there is a way to make gcc insert stack-unwind
directives that are honored across longjmp (I know C++ does it for
exceptions; so there may be a way, and I just don't know it).
Conversely, I do know that pthread_cleanup_push/pop, which does
something similar, is permitted by POSIX to NOT work across longjmp:

       Calling longjmp(3) (siglongjmp(3)) produces undefined  results
if  any
       call  has  been made to pthread_cleanup_push() or
pthread_cleanup_pop()
       without the matching call of the pair since the jump buffer was
filled
       by   setjmp(3)  (sigsetjmp(3)).   Likewise,  calling  longjmp(3)
(sig‐
       longjmp(3)) from inside a clean-up handler produces  undefined
results
       unless  the  jump  buffer  was  also filled by setjmp(3)
(sigsetjmp(3))
       inside the handler.


-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]