Scope Bound Resource Management with for Scopes

Resource management can be tedious in C. E.g to protect a critical block from simultaneous execution in a threaded environment you’d have to place a lock / unlock pair before and after that block:

pthread_mutex_t guard = PTHREAD_MUTEX_INTIALIZER;

pthread_mutex_lock(&guard);
// critical block comes here
pthread_mutex_unlock(&guard);

This is very much error prone since you have to provide such calls every time you have such a block. If the block is longer than some lines it is difficult to keep track of that, since the lock / unlock calls are spread on the same level as the other code.

Within C99 (and equally in C++, BTW) it is possible to extend the language of some sorts such that you may make this easier visible and guarantee that your lock / unlock calls are matching. Below , we will give an example of a macro that will help us to write something like

P99_PROTECTED_BLOCK(pthread_mutex_lock(&guard), 
    pthread_mutex_unlock(&guard)) {
       // critical block comes here
}

If we want to make this even a bit more comfortable for cases that we still need to know the mutex variable we may have something like:

GUARDED_BLOCK(guard) {
       // critical block comes here
}

The macro P99_PROTECTED_BLOCK can be defined as follows:

#define P99_PROTECTED_BLOCK(BEFORE, AFTER)                         \
for (int _one1_ = 1;                                               \
     /* be sure to execute BEFORE only at the first evaluation */  \
     (_one1_ ? ((void)(BEFORE), _one1_) : _one1_);                 \
     /* run AFTER exactly once */                                  \
     ((void)(AFTER), _one1_ = 0))                                  \
  /* Ensure that a `break' will still execute AFTER */             \ 
  for (; _one1_; _one1_ = 0)

As you may see, this uses two for statements. The first defines an auxiliary variable _one1_ that is used to control that the dependent code is only executed exactly once. The arguments BEFORE and AFTER are then placed such that they will be executed before and after the dependent code, respectively.

The second for is just there to make sure that AFTER is even executed when the dependent code executes a break statement. For other preliminary exits such as continue, return or exit there is unfortunately no such cure. When programming the dependent statement we have to be careful about these, but this problem is just the same as it had been in the “plain” C version.

Generally there is no run time performance cost for using such a macro. Any decent compiler will detect that the dependent code is executed exactly once, and thus optimize out all the control that has to do with our variable _one1_.

The GUARDED_BLOCK macro could now be realized as:

#define GUARDED_BLOCK(NAME)        \
P99_PROTECTED_BLOCK(               \
    pthread_mutex_lock(&(NAME)),   \
    pthread_mutex_unlock(&(NAME)))

Now, to have more specific control about the mutex variable we may use the following:

#define P99_GUARDED_BLOCK(T, NAME, INITIAL, BEFORE, AFTER)           \
for (int _one1_ = 1; _one1_; _one1_ = 0)                             \
  for (T NAME = (INITIAL);                                           \
       /* be sure to execute BEFORE only at the first evaluation */  \
       (_one1_ ? ((void)(BEFORE), _one1_) : _one1_);                 \
       /* run AFTER exactly once */                                  \
       ((void)(AFTER), _one1_ = 0))                                  \
    /* Ensure that a `break' will still execute AFTER */             \
    for (; _one1_; _one1_ = 0)

This is a bit more complex than the previous one because in addition it declares a local variable NAME of type T and initializes it.

Unfortunately, the use of static for the declaration of a for-scope variable is not allowed by the standard. To implement a simple macro for a critical section in programs that would not depend on any argument, we have to do a bit more than this.

Other block macros that can be implemented with such a technique:

  • pre- and postconditions
  • make sure that some dynamic initialization of a static variable is performed exactly once
  • code instrumentation

P99 now has a lot of examples that use this feature.

12 thoughts on “Scope Bound Resource Management with for Scopes”

  1. The trick is here (the 2 inner loops are like P99_BLOCK, the outer loop is the real one)

    #include 
    
    int main() {
    int a=0;
    for(a=0; a=0; ({ if (o==1) break; }))
        for (int i=0; !o && (o=1), i==0; o=-1, i=-1 )
      {
        printf("In loop %d o=%d i=%d\n",a, o, i);
        1==1 && (int)break;
      }
    }
    printf("Finished with %d\n", a);
    }
    
    1. In fact the code that you posted on your blog makes a bit more sense than what you posted here, there seem to be some parts missing.

      In any way, I see the idea. I think something along the lines

      for (int o = 0; o >=0;) if (o == 1) { break; } else
         for (int i=0; !o && (o=1), i==0; o=-1, i=-1 ) 
      

      would do the trick without gcc’s compound expression. (But would eventually introduce some bogus warnings about dangling else)

      But all of that has a major drawback, it only works inside a for, do, while or switch statement. So if you’d use such tricks in a macro, you’d need to have two versions one within such “breakable” statements and one outside.

      1. Will that really work? It looks like the break clause outside of the pranthesis will break the top “for” loop and not in the enclosing context

        #include <stdio.h>
        main() {
          int a;
          for (a=1; a<=2; a++) {
            printf(“Main context a=%d\n”, a);
            for (int o = 0; o >=0;) if (o == 1) { printf(“Detected break\n”); break; } else
              for (int i=0; !o && (o=1), i==0; o=-1, i=-1 ) { printf(“Inner context\n”); break; }
          }
        }
        

        detects the break but cannot propagate it:
        Main context a=1
        Inner context
        Detected break
        Main context a=2
        Inner context
        Detected break

        This works in gcc and breaks out of the enclosing context
        (for-loop of a):

        #include <stdio.h>
        main() {
          int a;
          for (a=1; a<=2; a++) {
            printf(“Main context a=%d\n”, a);
            for (int o = 0; o >=0; ({ if (o == 1) { printf(“Detected break\n”); break; } }) )
              for (int i=0; !o && (o=1), i==0; o=-1, i=-1 ) { printf(“Inner context\n”); break; }
          }
        }
        

        Main context a=1
        Inner context
        Detected break

        1. Yes, this would need an unhidden break that is directly visible to the surrounding context. But then again, this can’t be done in a macro that we want to place inside any kind of context.

          I will think of that.

          Jens

          (I also deleted the second version of your comment. The trick on WP sites is to have sourcecode in [sourcecode] special tags.)

      2. That gcc example working, suggests that a for loop is not equivalent to this while loop:

        expression-1 ;
        while ( expression-2) {
         statement
         expression-3 ;
        }
        

        expression-3 contains the break clause which should break the associated while loop, but in my gcc compound statement example it breaks the surrounding context not the associated for loop.

        1. yes, it seems that with their compound expression extension they interpret this weirdly in a way that would not have an easy equivalent in standard C.

          statement-1;
          if (false) {
           BREAKOUT: expression-3;
            goto BREAKIN; 
          }
          else while (expression-2) {
           statement-2
           goto BREAKOUT;
           BREAKIN:;
          }
          
      3. Hah! Your example there was the clue to propagating breaks.

        Thanks to your tip on the gcc equivalence using BREAKOUT and BREAKIN I can contrive a bunch of macros that expand to this:

        main() {
          int a;
          for (a=1; a<=2; a++) {
            printf("Main context a=%d\n", a);
        
            if (0) { break_line_6: break ; } else for (int o = 0; o >=0; ) if (o == 1) { printf("Detected break\n"); goto break_line_6; } else
              for (int i=0; !o && (o=1), i==0; o=-1, i=-1 ) { printf("Inner context\n"); break; }
          }
        }
        

        it does not require gcc-isms – sadly the C style is lacking but it functions as desired

        #define GENSYM_CONCAT(name, salt) name ## salt
        #define GENSYM2(name, salt) GENSYM_CONCAT(name, salt)
        #define GENSYM(name) GENSYM2(name, __LINE__)
        
        #define _BRACELESS_SCOPE(__label__, _declaration_) \
            if (0) { __label__: break ; } else for (int _o = 0; _o >=0; ) if (_o == 1) goto __label__; else \
              for (_declaration_; !_o && (_o=1); _o=-1) \
        
        #define BRACELESS_SCOPE(_declaration_) _BRACELESS_SCOPE(GENSYM(break_line_) , _declaration_)
        
        main() {
          int a;
          for (a=1; a<=2; a++) {
            printf("Main context a=%d\n", a);
        
            BRACELESS_SCOPE(char* message="hello") { printf("Inner context: %s\n", message); break; }
          }
        }
        

        The main deficiency is that a single macro cannot cannot ultimately expand to more than one of these constructs, nor can it be used twice on the same line, or the generated labels will conflict. __COUNTER__ where supported could be passed instead of __LINE__. I guess this is something you already addressed in P99 if I look again…

        1. There are some things that you might want to look into in “p99_id.h”. But it wouldn’t really help you out here, you’d have to assure that all these constructs end on different lines. (And gcc and clang, e.g, already don’t agree what a line in the preprocessor context is, they number differently.)

          If you use that concept as a sub-macro for different things you could at least take care that the label part is tagged with a different suffix for each of these uses. There is already P00_BLK_DECL_STATIC that proceeds like this.

          As far as I can see, your macro still has some drawbacks

          • The declaration context of for loops is restricted.
            • It doesn’t allow for storage classes other than auto or register. So no static or _Thread in there.
            • It doesn’t allow for declarations that also declare a type.
          • Your construct can’t be used in an arbitrary function context, because it exposes a break. I would suggest a different naming to mark this clearly: something like BREAKABLE_SCOPE. BTW BRACELESS_SCOPE as a name clearly doesn’t capture it well, since the depending statement may be a block, and thus have its own scope.
  2. Am I missing something, or this code would also do the trick ?

    #define PROTECTED_BLOCK(BEFORE, AFTER)     \
      for (int _i_ = 0; _i_ &lt; 1; AFTER)     \
        for (BEFORE; _i_ &lt; 1; _i_++)
    

    (I'm not doing the void thing, but you can also do it)

    1. That would be much simpler, agreed, but unfortunately it is more error prone. That version wouldn’t tolerate a break statement that slips into the depending block. If there’d be such a thing your construct would never advance the loop counter.

Comments are closed.