Choosing an Appropriate FIFO Data Structure - c++

I need a FIFO structure that supports indexing. Each element is an array of data that is saved off a device I'm reading from. The FIFO has a constant size, and at start-up each element is zeroed out.
Here's some pseudo code to help understand the issue:
Thread A (Device Reader):
1. Lock the structure.
2. Pop oldest element off of FIFO (don't need it).
3. Read next array of data (note this is a fixed size array) from the device.
4. Push new data array onto the FIFO.
5. Unlock.
Thread B (Data Request From Caller):
1. Lock the structure.
2. Determine request type.
3. if (request = one array) memcpy over the latest array saved (LIFO).
4. else memcpy over the whole FIFO to the user as a giant array (caller uses arrays).
5. Unlock.
Note that the FIFO shouldn't be changed in Thread B, the caller should just get a copy, so data structures where pop is destructive wouldn't necessarily work without an intermediate copy.
My code also has a boost dependency already and I am using a lockfree spsc_queue elsewhere. With that said, I don't see how this queue would work for me here given the need to work as a LIFO in some cases and also the need to memcpy over the entire FIFO at times.
I also considered a plain std::vector, but I'm worried about performance when I'm constantly pushing and popping.

One point not clear in the question is the compiler target, whether or not the solution is restricted to partial C++11 support (like VS2012), or full support (like VS2015). You mentioned boost dependency, which lends similar features to older compilers, so I'll rely on that and speak generally about options on the assumption that boost may provide what a pre-C++11 compiler may not, or you may elect C++11 features like the now standardized mutex, lock, threads and shared_ptr.
There's no doubt in my mind that the primary tool for the FIFO (which, as you stated, may occasionally need LIFO operation) is the std::deque. Even though the deque supports reasonably efficient dynamic expansion and shrinking of storage, contrary to your primary requirement of a static size, it's main feature is the ability to function as both FIFO and LIFO with good performance in ways vectors can't as easily manage. Internally most implementations provide what may be analogized as a collection of smaller vectors which are marshalled by the deque to function as if a single vector container (for subscripting) while allowing for double ended pushing and popping with efficient memory management. It can be tempting to use a vector, employing a circular buffer technique for fixed sizes, but any performance improvement is minimal, and deque is known to be reliable.
Your point regarding destructive pops isn't entirely clear to me. That could mean several things. std::deque offers back and front as a peek to what's at the ends of the deque, without destruction. In fact, they're required to look because deque's pop_front and pop_back only remove elements, they don't provide access to the element being popped. Taking an element and popping it is a two step process on std::deque. An alternate meaning, however, is that a read only requester needs to pop strictly as a means of navigation, not destruction, which is not really a pop, but a traversal. As long as the structure is under lock, that is easily managed with iterators or indexes. Or, it could also mean you need a independent copy of the queue.
Assuming some structure representing device data:
struct DevDat { .... };
I'm immediately faced with that curious question, should this not be a generic solution? It doesn't matter for the sake of discussion, but it seems the intent is an odd combination of application specific operation and a generalized thread-safe stack "machine", so I'll suggest a generic solution which is easily translated otherwise (that is, I suggest template classes, but you could easily choose non-templates if preferred). These psuedo code examples are sparse, just illustrating container layout ideas and proposed concepts.
class SafeStackBase
{ protected: std::mutex sync;
};
template <typename Element>
class SafeStack : public SafeStackBase
{ public:
typedef std::deque< Element > DeQue;
private:
DeQue que;
};
SafeStack could handle any kind of data in the stack, so that detail is left for Element declaration, which I illustrate with typedefs:
typedef std::vector< DevDat > DevArray;
typedef std::shared_ptr< DevArray > DevArrayPtr;
typedef SafeStack< DevArrayPtr > DeviceQue;
Note I'm proposing vector instead of array because I don't like the idea of having to choose a fixed size, but std::array is an option, obviously.
The SafeStackBase is intended for code and data that isn't aware of the users data type, which is why the mutex is stored there. It could easily part of the template class, but the practice of placing non-type aware data and code in a non-template base helps reduce code bloat when possible (functions which don't use Element, for example, need not be expanded in template instantiations). I suggest the DevArrayPtr so that the arrays can be "plucked out" of the queue without copying the arrays, then shared and distributed outside the structure under shared_ptr's shared ownership. This is a matter of illustration, and does not adequately deal with questions regarding content of those arrays. That could be managed by DevDat, which could marshal reading of the array data, while limiting writing of the array data to an authorized friend (a write accessor strategy), such that Thread B (a reader only) is not carelessly able to modify the content. In this way it's possible to provide these arrays without copying data..just return a copy of the DevArrayPtr for communal access to the entire array. This also supports returning a container of DevArrayPtr's supporting ThreadB point 4 (copy the whole FIFO to the user), as in:
typedef std::vector< DevArrayPtr > QueArrayVec;
typedef std::deque< DevArrayPtr > QueArrayDeque;
typedef std::array< DevArrayPtr, 12 > QueArrays;
The point is that you can return any container you like, which is merely an array of pointers to the internal std::array< DevDat >, letting DevDat control read/write authorization by requiring some authorization object for writing, and if this copy should be operable as a FIFO without potential interference with Thread A's write ownership, QueArrayDeque provides the full feature set as an independent FIFO/LIFO structure.
This brings up an observation about Thread A. There you state lock is step 1, while unlock is step 5, but I submit that only steps 2 and 4 are really required under lock. Step 3 can take time, and even if you assume that is a short time, it's not as short as a pop followed by a push. The point is that the lock is really about controlling the FIFO/LIFO queue structure, and not about reading data from the device. As such, that data can be fashioned into DevArray, which is THEN provided to SafeStack to be pop/pushed under lock.
Assume code inside SafeStack:
typedef std::lock_guard< std::mutex > Lock; // I use typedefs a lot
void StuffIt( const Element & e )
{ Lock l( sync );
que.pop_front();
que.push_back( e );
}
StuffIt does that simple, generic job of popping the front, pushing the back, under lock. Since it takes an const Element &, step 3 of Thread A is already done. Since Element, as I suggest, is a DevArrayPtr, this is used with:
DeviceQue dq;
auto p = std::make_shared<DevArray>();
dq.StuffIt( p );
How the DevArray is populated is up to it's constructor or some function, the point is that a shared_ptr is used to transport it.
This brings up a more generic point about SafeStack. Obviously there is some potential for standard access functions, which could mimic std::deque, but the primary job for SafeStack is to lock/unlock for access control, and do something while under lock. To that end, I submit a generic functor is sufficient to generalize the notion. The preferred mechanics, especially with respect to boost, is up to you, but something like (code inside SafeStack):
bool LockedFunc( std::function< bool(DevQue &)> f )
{
Lock l( sync );
f( que );
}
Or whatever mechanics you like for calling a functor taking a DevQue as a parameter. This means you could fashion callbacks with complete access to the deque (and it's interface) while under lock, or provide functors or lambdas which perform specific tasks under lock.
The design point is to make SafeStack small, focused on that minimal task of doing a few things under lock, taking most any kind of data in the queue. Then, using that last point, provide the array under shared_ptr to provide the service of Thread B steps 3 and 4.
To be clear about that, keep in mind that whatever is done to the shared_ptr to copy it is similar to what can be done to simple POD types, like ints, with respect to containers. That is, one could loop through the elements of the DevQue fashioning a copy of those elements into another container in the same code which would do that for a container of integers (remember, it's a member function of a template - that type is generic). The resulting work is only copying pointers, which is less effort than copying entire arrays of data.
Now, step 4 isn't QUITE clear to me. It appears to say that you need to return a DevArray which is the accumulated content of all entries in the queue. That's trivial to arrange, but it might work a little better with a vector (as that's dynamically expandable), but as long as the std::array has sufficient room, it's certainly possible.
However, the only real difference between such an array and the queue's native "array of arrays" is how it is traversed (and counted). Returning one Element (step 3) is quick, but since step 4 is indicated under lock, that's a bit more than most locked functions should really do if they don't have to.
I'd suggest SafeStack should be able to provide a copy of que (a DeQue typedef), which is quick. Then, outside of the lock, Thread B has a copy of the DeQue ( a std::deque< DevArrayPtr > ) to fashion into it's own "giant array".
Now, more about that array. To this point I've not adequately dealt with marshalling it. I've just suggested that DevDat does that, but this may not be adequate. Certainly the content of the std::array or std::vector conveying a collection of DevDats could be written. Perhaps that deserves it's own outer structure. I'll leave that to you, because the point I've made is that SafeStack is now focused on it's small task (lock/access/unlock) and can take anything which can be owned by a share_ptr (or POD's and copyable objects). In the same way SafeStack is an outer shell marshalling a std::deque with a mutex, some similar outer shell could marshal read only access to the std::vector or std::array of DevDats, with a kind of write accessor used by Thread A. That could be a simple as something that only allows construction of the std::array to create it's content, after which read only access could be all that's provided.

I would suggest you to use boost::circular_buffer which is a fixed size container that supports random access iteration, constant time insert and erase at the beginning and end. You can use it as a FIFO with push_back(), read back() for the latest data saved and iterate over the whole container via begin(), end() or using operator[].
But at start-up the elements are not zeroed out. It has in my opinion an even more convenient interface. The container is empty at first and insertion will increase size until it reaches max size.

Related

May I clear a priority_queue by clearing its underlying container?

The following code inherits std::priority_queue and provides clear() which calls the internal std::vector's clear()
#include<iostream>
#include<queue>
using namespace std;
template<class type>
struct mypq :public priority_queue<type> {
void clear(){
this->c.clear();
}
};
mypq<int>pq;
int main() {
for(int i=0;i<10;++i)
pq.push(i);
pq.clear();
for(int j=-5;j<0;++j)
pq.push(j);
while (!pq.empty()){
cerr<<pq.top()<<endl;
pq.pop();
}
}
When I tested it with g++, MSVC++ and clang, it produces the expected output:
-1
-2
-3
-4
-5
But I haven't seen any guarantee for this, i.e. clearing the internal vector will be the same as calling pop() when the priority_queue isn't empty. Although I know other ways to clear it such as swap or assign it using an empty priority_queue, I think if this code can work well, it would be more efficient since the allocated memory in the vector is reusable. So I wonder if this code is portable or won't always work?
But I haven't seen any guarantee for this, i.e. clearing the internal vector will be the same as calling pop() when the priority_queue isn't empty.
Because that's not the same thing. A std::priority_queue is a specifically designed container adaptor that keeps things ordered via strict weak ordering. If you don't specify the type of container the queue will have (which you don't in the example), then the default type is a std::vector.
So calling pop() on a non-empty priority queue will have the effect of removing the top element from the queue while calling clear() on the underlying container will remove all elements from the queue, not just the top most.
Although I know other ways to clear it such as swap or assign it using an empty priority_queue, I think if this code can work well, it would be more efficient since the allocated memory in the vector is reusable. So I wonder if this code is portable or won't always work?
According to the reference, the underlying c member object is protected, so accessing the way you are should be guaranteed across compilers, that is, calling this->c.clear(); should be portable (anecdotally, it works on g++ 4.2.1 on an older version of OpenBSD).
As far as efficiency is concerned, it would somewhat depend. Doing something like this->c.clear(); vs. q = priority_queue <int>(); might not be that different in terms of memory usage or complexity, though you would have to test it on the different platforms to verify. However, doing something like this->c.clear(); vs. while(!q.empty()) { q.pop(); }, would be more efficient.
In terms of memory efficiency, the pop function of the priority queue calls the underlying containers pop_back function, and neither the pop_back nor the clear affect the underlying vector's capacity, so there's not really any "savings" to be had in that way; though with this, you could resize the vector to increase/decrease capacity if you had a specific need to.
Just remember that the priority queue pop function calls the underlying containers pop_back function, and calling pop_back on an empty container is undefined behavior.
Hope that can help.
A very good question. While I can't seem to find any strict guarantee that it is a correct method, there are some reasons to think that it is.
For example, consider the docs for operator=:
Copy assignment operator. Replaces the contents with a copy of the
contents of other. Effectively calls c = other.c;. (implicitly
declared)
Since the other queue may be of a different size, this essentially means that there is no internal state that is dependent on size. Moreover, it also implies that assigning an empty queue essentially replaces the container with an empty one and does nothing else.
With that in mind, and the fact that a reasonable implementation of a priority queue would hardly need to maintain any state except for the size of the queue, I believe it can be safely assumed that clearing the underlying container is a valid way to empty the queue.

Observer with multiple subjects using std::unordered_set

I have seen implementations of the Observer design pattern in which the Observer is responsible for multiple Subjects. Most of these implementations use a std::vector<Subject*> in order to keep track of the Subjects.
Would it be possible for me to do a similar thing, using a std::unordered_set<weak_ptr<Subject>> instead?
The reason I want to use an unordered_set is that I will not need duplicates, and I don't need an ordered container. From what I understand, an unordered_set is the way to go in this situation. Also, the reason I am using a weak_ptr is that it should be safer?
If you disagree, leave an answer explaining what container I should use instead. If I did use the unordered_set, I would have to declare a hash function for the weak_ptr, but could this be accomplished by just using the hash function for the pointer inside, obtained with subjects.lock().get()?
First of all, in my answer I will use Subject as the one who sends messages to registered Observers, since it is the common use of this two terms.
Would it be possible for me to do a similar thing, using a std::unordered_set<weak_ptr<Observer>> instead?
It is possible. However remeber that the object held by a weak_ptr can be freed, weak_ptr needs to be casted to a shared_ptr before accessing the underlying object. It is done this way so the object is not freed while you are handling it.
Would it be possible for me to do a similar thing, using a std::unordered_set> instead?
If you need to enforce uniqueness the unordered_set looks like a good choice to me. If you don't need to, then a vector is more straightforward solution. Some would tell that unique_set is slower and requires more memory than a vector, but unless you need very high frequency registration of Observers or thousands of them, you won't notice the difference.
About the weak pointer, it gives you the flexibility of having your Observers deallocated while registered, so it should be fine. This behaviour may be unexpected if you come from a memory managed language like Java. If you want to hold them in existence while they are registered in your Subject you may use a shared_pointer instead.
I would have to declare a hash function for the weak_ptr, but could this be accomplished by just using the hash function for the pointer inside, obtained with observer.lock().get()?
Be careful when creating hash functions, I dont recommend you to use object's pointer for the hash function, specially if your Subjects can be copied/moved. Instead you may create an unique identifier for every Subject upon creation using a counter, and remember to write copy/move constructors and operators accordingly.
If you cannot write an identifying hash function, then you should't use the unique_set, since you lose the advantages it brings.
As a footnote, the beauty of object containers is that you can fit them to your needs, every solution is the correct solution if it does what you really want.
There isn't really one 'correct' answer for the choice of container; it depends upon what you are aiming for from the point of view of performance. And whether or not performance is really all that important for this.
It also depends upon memory efficiency. If you only have a few of these unordered_set objects and need very fast lookup then it may be a good choice. Since it is a hash table it will use a fairly large amount of memory per unordered_set object.
If you have a lot of unordered_set objects with fairly few items then it may get a bit expensive in terms of memory budget. If you need fast insertion and removal then std::set may be better in this case. However if the collection only ever contains a handful of items then the lookup will probably actually be faster with a linear search of std::vector due to the processor cache (i.e. better locality of reference of the vector elements compared to std::set - which may result in more elements being on the same cache line). Memory usage of vector will be lower than either of std::set or std::unordered_set.
If you need fast lookup of specific objects for some reason and use std::vector and typically have a a moderate number of elements then you could insert items into the vector in sorted order. You can then use std::lower_bound to do an O(log n) binary search lookup. However, this has a potentially high cost for inserting and removing elements.
I'd probably just go for std:vector in most cases - you generally have few observers, so may as well keep memory usage tighter.
And using weak_ptr is certainly a good option if these objects are used elsewhere with shared_ptr.
In the Observer pattern the Observer subscribes to notifications about changes on a Subject. The Subject is responsible for updating all subscribed Observers whenever its observable state changes. For that to work the Observers do not need to keep track of the Subjects. Instead the Subject must keep track of all subscribed Observers.
A nice explanation of the Observer pattern can be found here: https://sourcemaking.com/design_patterns/observer
Code outline:
class Subject;
class Observer
{
public:
// when notified about a change, the Observer
// knows which Subject changed, because of the parameter s
virtual void subjectChanged(Subject* s)=0;
};
class Subject
{
private:
int m_internalState;
std::set<Observer*> m_observers;
public:
void subscribe(Observer* o)
{
m_observers.insert(o);
}
void unsubscribe(Observer* o);
{
m_observers.erase(o);
}
void setInternalState(int state)
{
auto end=m_observers.end();
for(auto it=m_observers.begin(); it != end; ++it)
it->subjectChanged(this);
}
};
In most cases it won't matter which exact collection type you choose for storing the Observers, because there will be very few Observers. However, choosing a set-type has the advantage, that each Observer will receive only one notification. With a vector it could happen that the same Observer receives multiple notifications about the same change, if (for some reason) it was subscribed multiple times.
I really think that using std::unordered_set is a bit over-kill.
what is this observer pattern? when an event or a change of state occurs, iterate over an array of state-checkers and make them do something if the state is invalid or special in any sort.
this has being said, you want to iterate over an array with objects with overriden virtual function and call it. why whould set give us any benefit?
also, I don't get the weak_ptr idea - the owner of the observers is the array with holds them. the owner of that array is the Subject.
now that all has being said, I would go with std::vector<std::unique_ptr<Observer>>.
Edit:
using C++11, I'd even go with std::vector<std::function<void(Subject&)>> and avoid the boilerplate of inheriting+overriding.
The simplest thing to do is to roll with boost::signals2, which already implemented this for you, for all signatures. The fundamental problem with your approach is that the implementation is tied to a particular signature with a particular subject and observer, which is virtually worthless compared to a generic solution that applies for all cases.
The Observer pattern is not a pattern, it's a class template.

Tradeoffs when returning a collection

There are various ways of returning a collection of items from a method of a class in C++.
For example, consider the class MessageSpy that listens on all Messages sent over a connection. A client could access the messaging information in a number of ways.
const CollectionClass MessageSpy::getMessages()
Iterator MessageSpy::begin(), Iterator MessageSpy::end()
void MessageSpy::getMessages(OutputIterator)
void MessageSpy::eachMessage(Functor)
others...
Each approach has its trade-offs. For example: Approach 1 would require copying the whole collection which is expensive for large collections. While approach 2 makes the class look like a collection which is inappropriate for a view...
Since I'm always strungling choosing the most appropriate approach in I wonder what you consider the trade-offs/costs when considering these approaches?
I suggest an iterator based/callback based approach in cases where you demand the most lightweight solution possible.
The reason is that it decouples the supplier from the usage patterns by the consumer.
In particular, slamming the result into a collection1 (even though the result maybe "optimized" - likely into (N)RVO or moving instead of copying the object) would still allocate a complete container for the full capacity.
Edit: 1 an excellent addition to "obligatory papers" (they're not; they're just incredibly helpful if you want to understand things): Want Speed? Pass By value by Dave Abrahams.
Now
this is overkill if the consumer actually stops processing data after the first few elements
for(auto f=myType.begin(), l=myType.end(); f!=l; ++f)
{
if (!doProcessing(*f))
break;
}
this can be suboptimal even if the consumer processes al elements eventually: there might not be a need to have all elements copied at any particular moment, so the 'slot' for the 'current element' can be reused, reducing memory requirements, increasing cache locality. E.g.:
for(auto f=myType.begin(), l=myType.end(); f!=l; ++f)
{
myElementType const& slot = *f; // making the temp explicit
doProcessing(slot);
}
Note that iterator interfaces are simply still superior if the consumer did want a collection containing all elements:
std::vector<myElementType> v(myType.begin(), myType.end());
// look: the client gets to _decide_ what container he wants!
std::set<myElementType, myComparer> s(myType.begin(), myType.end());
Try getting this flexibility otherwise.
Finally, there are some elements of style:
by nature it's easy to expose (const) references to the elements using iterators; this makes it much easier to avoid object slicing and to enable clients to use the elements polymorphically.
iterator-style interfaces could be overloaded to return non-const references on dereference. A container to be returned, couldn't contain references (directly)
if you adhere to the requirements of range-based-for in C++11 you can have some syntactic sugar:
for (auto& slot : myType)
{
doProcessing(slot);
}
Finally, (as shown above), in the general sense iterators work nicely with the standard library.
The callback style (and similarly the Output-iterator style) has many of the benefits of the iterator style (namely, you could use return values to abort iteration halfway, and you could do processing without allocating copies of all elements up front), but it seems to me to be slightly less flexible in use. Of course, there may be situations where you want to encourage a particular usage pattern, and this migh be a good way to go.
The first thing (you somehow didn't mention at all) I would think about is
const CollectionClass& MessageSpy::getMessages()
Note the &. That returns you const reference which can't be modified but can be freely accepted.
No copying, unless you really want to copy.
If that's not suitable, Qt, for example, uses "implicit data sharing" for plenty of classes.
I.e. your classes are "kinda" returned by value, BUT their internal data is shared until you attempt to perform write operation on one of them. In this case, class you're trying to write into, performs a deep copy, and data stops being shared. That means less data is moved around.
And there's return value optimization some people on SO seems to love too much. Basically, when you return something big by value, some compilers in certain situations can eliminate extra copy, and immediately pass value bypassing extra assignment which may be faster than returning by reference. I wouldn't rely on it too much, but if you profiled your code and figured out that using RVO provides a good speed-up, then it is worth using.
I wouldn't recommend "iterators", because using them on C++03 compiler without auto keyword is royal pain in the #&#. Long names or many typedefs. I would return const reference to container itself instead.

how to put std::string into boost::lockfree::queue (or alternative)?

I'm trying to put std::strings into boost::lockfree::queues so that my threads can update each other with new data.
When I try to use boost::lockfree::queue<std::string> updated_data;, g++ says :
In instantiation of 'class boost::lockfree::queue >':
error: static assertion failed: (boost::has_trivial_destructor::value)
error: static assertion failed: (boost::has_trivial_assign::value)
I've been shown generally what these errors mean, but I have no hope of ever fixing this myself, as I'm almost brand new to c++.
Is there an alternative way to pass text data between threads with lockfree? If not, please show me how to put std::string into a boost::lockfree::queue.
The boost::lockfree::queue documentation clearly states the the contained itemm must have a trivial copy assignment and destructor, which std::string doesn't have.
If you have a single producer and a single consumer you can use spsc_queue (http://www.boost.org/doc/libs/1_54_0/doc/html/boost/lockfree/spsc_queue.html) which requires only default constructability and copyability.
If you have multiple producers or consumers you're going to be stuck with a normal locking queue (or a custom string that doesn't use dynamic allocation).
I have no hope of ever fixing this myself, as I'm almost brand new to c++.
Then I have to wonder why you're messing with things like lockfree queues.
Is there an alternative way to pass text data between threads with lockfree?
Yes, you could just store a std::string* pointer to the data in the queue, because a pointer is a trivial type and so is allowed in the queue. Equivalently, you could store a reference_wrapper<std::string>. The problem with that is you need to store the strings somewhere else, in order to be able to point to them, so now all you've done is move the problem to somewhere else (e.g. you could maintain a list of strings in each thread, and store pointers to the externally-managed string in the lock-free queue, but you don't know when it's safe to remove a string from the per-thread list so it grows and grows.)
I would suggest you use a simple std::queue<std::string> and do your own synchronisation with a boost::mutex and boost::condition_variable, or find an existing implementation of a thread-safe (not lock-free!) queue.
If you put raw pointers in the queue, the old std::strings will be leaked, since there is no way to free them when they are no longer needed. This is because there is no way to free the objects in a thread-safe way without taking a lock (other than some tricks like hazard pointers, which boost::lockfree::queue does not use)
For technical reasons I do not really understand, the boost::lockfree::queue requires a trivial assignment operator and a trivial destructor, which means that your object cannot be nor contain any data type that must free memory in its destructor, like std::string.

Returning an iterator in a multi threaded environment, a good idea?

Is it a good idea to return an iterator on a list in an object that is used and shared in a multi threaded environment?
class RequestList
{
public:
RequestList::RequestList();
RequestList::~RequestList();
std::list<boost::shared_ptr<Request> >::iterator GetIterator();
int ListSize();
void AddItem(boost::shared_ptr<Request> request);
void RemoveItem(boost::shared_ptr<Request> request);
std::list<boost::shared_ptr<Request> > GetRequestsList();
boost::shared_ptr<Request> GetRequest();
private:
std::list<boost::shared_ptr<Request> > requests;
std::list<boost::shared_ptr<Request> >::iterator iter; //Iterator
boost::mutex listmtx;
};
std::list<boost::shared_ptr<Request> >::iterator RequestList::GetIterator()
{
return this->iter;
}
USE:
RequestList* requests;
In some thread (may be used again in other threads)
std::list<boost::shared_ptr<Request> >::iterator iter = requests->GetIterator();
Or would it be smarter to just create an iterator for that list each time and use it locally within each thread?
No it is usually not a good idea to share an iterator across threads. There are a couple of ways to make sure you don't get in trouble.
First off, an iterator is a light-weight object which is fast to construct and takes up very little memory. So you should not be concerned about any performance-issues. Just create an instance whenever you need one.
That said you do have to make sure that your list is not altered when you are iterating. I see you have a boost::mutex on in your class. Locking that will be perfect for ensuring that you don't get any problems when iterating.
A different and equally valid way of handling these situations is to simply copy the internal list and iterate that. This is a good solution if you require that the list is continually updated and you don't want other threads waiting. Of course it takes up a bit more memory, but since you are storing smart pointers, it will hardly be anything at all.
Depends how the list is used, but from what you've shown it looks wrong. The iterator becomes invalid if the element it refers to is removed: the element in this case being the shared_ptr object in the list.
As soon as you release the mutex, I guess some other thread could come along and remove that element. You haven't shown code that does it, but if it can happen, then iterators shouldn't "escape" the mutex.
I assume this is a "self-synchronizing" container, since the mutex is private and there's nothing in the API to lock it. The fundamental difficulty with such things, is that it's not thread-safe to perform any kind of iteration on them from the outside. It's easy enough to provide a thread-safe queue, that supports:
adding an element,
removing an element by value,
removing the head and returning a copy of its value.
Beyond that, it's harder to provide useful basic operations, because almost anything that manipulates the list in any interesting way needs to be done entirely under the lock.
By the looks of things, you can copy the list with GetRequestsList, and iterate over the copy. Not sure whether it will do you any good, since the copy is instantly out of date.
Accessing the list via iterators in multiple threads where the main list itself is not locked is dangerous.
There's no guarantee what state the list will be as you do things in with the iterators in different threads (for example, one thread could happily iterate through and erase all the items, what will the other thread - who's also iterating, see?)
If you are going to work on the list in multiple threads, lock the whole list first, then do what you need to. Copying the list is an option, but not optimal (depending on the size of your list and how fast it's updated). If locking becomes a bottle neck, re-think your architecture (list per thread for example?)
Each thread that calls the GetIterator function will get its own copy of the stored iterator in the list.
As a std::list<>::iterator is a bi-directional iterator, any copies you make are completely independent of the source. If one of them changes, this will not be reflected in any of the other iterators.
As for using iterator in a multi-threaded environment, this is not that different from a single-threaded environment. The iterator remains valid as long as the element it refers to is part of the container. You just have to take care of proper synchronization when accessing/modifying the container or its elements.
If the list is modified by one of your threads you might get into trouble.
But of course, you can take care of that by setting locks and ro- and rw-locks during modification. But since mutexes are the scurge of any high performance program, maybe you can make a copy of the list (or references) and keep the original list mutex- and lock-free? That would be the best way.
If you have the mutexes in place you only have to battle with the issues of a modifying a list while holding iterators on it as you would normally do anyway -- i.e. adding elements should be ok, deletion should be done "careful" but doing it on a list is probably less likely to explode as on a vector:-)
I would reconsider this design and would use a task-based approach. This way you don't need any mutexes.
For example use Intel TBB, which initializes a task pool internally. So you can easily implement a one-writer/multiple-readers concept.
First add all requests to your request container (a simple std::vector might be better suited in terms of cache locality and performance) and then do a parallel_for() over you request-vector BUT DON'T remove a request in your parallel-for() functor!
After the processing you can actually clear your request vector without any need of locking a mutex. That's it!
I hope I could help a bit.