tags:

views:

193

answers:

5

Hi,

Have you had any case when RAII wasn't the best method for resource management?

Just couriosity...

Thanks.

A: 

GC can handle the memory of cyclic data structures for the programmer while RAII will require the programmer to manually break the cycle somewhere.

jk
Can you please give an example?
IUnknownPointer
Identifying which in-memory objects are garbage is relatively easy, irrespective of cycles. Determining a valid destruction order in the presence of cycles is hard. GC languages solve this problem by *not* solving it - they declare finalizers to be not guaranteed to run, so cleanups involving resources other than memory must be handled manually, meaning you have a problem of the same lifetime-managing form as the one that GC is supposed to fix. This is a good answer, though - if the only non-trivial-to-manage resource is memory, GC is better than RAII, which isn't that uncommon.
Steve314
indeed, the coice is between manually managing non-memory resources or manually managing cycles in your object graph. imho its usually not clear which is best because usually both hard to manage scarce resources and cyclic data structures are less common.
jk
Also note that RAII and the cycle problem are unrelated issues. The cycle problem is related to reference counting, that is just one of the possible RAII strategies. You can hold a double linked list with forward `shared_ptr` and backward `weak_ptr` and you will both be using RAII and not having issues with the cycles.
David Rodríguez - dribeas
As @David said, RAII is much more than ref counting. Shared pointers are ridiculously overused. They should not be the default choice.
jalf
That was kind of the point. there is no one size fits all solution so there is some manual analysis + chocie of smart pointers to use.
jk
+1  A: 

Sometimes two-stage initialization (create, then init, then use) is needed.

Or even three-stage: in our product, there is a collection of independent objects, each running a thread and able to subscribe to any number of other objects (including itself) via priority-inheriting queues. Objects and their subscriptions are read from the config file at startup. At construction time, each object RAIIs everything it can (files, sockets, etc), but no object can subscribe to others because they are constructed in unknown order. So then after all objects are constructed there's the second stage where all connections are made, and third stage when, once all connections are made, the threads are let go and begin messaging. Likewise, shutdown is mutli-stage as well.

Cubbi
My immediate reaction here is that each degree of initialisation could be a resource in itself. An object to abstract that resource might do little other than call methods on a referenced object on construction and destruction. "Possible" isn't the same as "good idea", though. Multi-stage initialisation and cleanup (a simple finite state model) is a good approach for some problems.
Steve314
A: 

RAII means that the ownership of resources is defined and managed through the guarantees provided by the language constructs, most notably, but not limited to, constructors and destructors.

The point of RAII in C++ is that the resource ownership policy can actually be enforced by the language. A lesser alternative to RAII is for the API to advise the caller (e.g., through comments or other documentation) to explicitly perform ACQUIRE() and RELEASE() operations at certain times. That kind of policy is not enforceable by the language.

So the original question is another way to ask whether there are cases when an unenforceable approach to resource management is preferable to RAII. The only cases I can think of are where you are deliberately circumventing the existing resource management constructs in the language, and writing your own framework. For example, you are implementing a garbage collected scripting language interpreter. The "virtual allocation" of atoms will likely play games with memory blocks. Similarly, a pool based allocator expects the program to eventually call a DESTROY_POOL() operation, with global consequences (i.e., any item allocated from that pool will be invalidated).

John
+1  A: 

The only case I can think of where RAII was not the solution is with multithreaded critical region lock management. In general it is advisable to acquire the critical region lock (consider that the resource) and hold it in a RAII object:

void push( Element e ) {
   lock l(queue_mutex);  // in construction acquire, in destruction release
   queue.push(e);
}

But there are situation where you cannot use RAII for that purpose. In particular, if a variable used in a loop condition is shared by multiple threads, and you cannot hold the lock for the whole loop execution, then you must acquire and release the lock with a different mechanism:

void stop_thread() {
   lock l(control_mutex);
   exit = true;
}
void run() {
   control_mutex.acquire();
   while ( !exit ) { // exit is a boolean modified somewhere else
      control_mutex.release();
      // do work
      control_mutex.acquire();
   }
   control_mutex.release();
}

It might even be possible to use RAII by (ab)using operator, now that I think of, but I had never actually thought of it. But I guess this is not really natural:

void run() {
   while ( lock(control_mutex), !exit ) {
      // do work
   }
}

So I guess that the answer is that not that I can imagine...

EDIT: Other solutions for the same problem using RAII:

@Mark Ranson:

bool should_exit() const {
   lock l(mutex);
   return exit;
}
void run() {
   while ( !should_exit() ) {
      // do work
   }
}

@fnieto:

void run() {
   while (true) {
      {  lock l(mutex);
         if (exit) break;
      }
      // do work
   }
}
David Rodríguez - dribeas
Before someone asks, that usage of `operator,` with a temporary is guaranteed by 5.18[expr.comma]/1: "All side effects (1.9) of the left expression, except for the destruction of temporaries (12.2), are performed before the evaluation of the right expression."
David Rodríguez - dribeas
Rather than querying the flag directly in the loop, couldn't you put it in a function that wraps the flag access within the RAII lock?
Mark Ransom
@Mark: right, you can. Also a coworker (@fnieto) suggested a different approach: `while (true) { { lock l(mutex); if (exit) break; } ... }` that again uses RAII and is *easier* to read than the `operator,` use. This is quite similar to your suggestion in that they move the check outside of the loop condition so that it can be enclosed in its own scope.
David Rodríguez - dribeas
A: 

In cases where resource release may fail, RAII may not be sufficient to manage that resource (since destructors shouldn't throw). RAII may still be part of that solution though.

Mark B