Great question Mike!
A mutex is required when ever you have a data structure that can be modified by more than one entity.
That is the short answer.
These mutexes do not have to be explicit! LV uses mutexes "behind the scenes" to ensure non-re-entrant VI's are not executing at the same time.
So if I understand your "such as a VI that Reads-Modifies-Writes a global variable" as a VI that shared by all of your VIT's that is the one and only method used to modify the global, then your global data is being protected by the mutex that protect the VI!
This is one of the benefits that come along with the "functional global".
RE: the dll calls.
The call library function will default to the user interface thread. The user interfacte thread is single threaded so this ensures there is only one call to to the dll at a time. If you re-configure the nodes then this does not apply.
You can combine the two above thoughts to be able to run a dll in a thread other than the user interface and still ensure only one call at a time. You put the dll call in a non-re-entrant VI. This will harness that same feature to ensure the is only one caller of the dll while allowing it to run in a thread other than the user interface.
So...
Most of the time I can get away without using mutexes (mutexi?) becuase I make extensive use of functional globals.
Now let me share how I architect LV app's and convince myself that I do run into the issues you have raised.
LV uses the data flow paradym so I design my aap's around the data, rather than around the process.
I will start defining my data objects based on the app's requirements.
These data objects can be globals, files, registers, GUI objects, etc. When I have the ability to define these objects (i.e. I can not define device status and control register formats) I will use techniques similar to what is used in DB design, keeping data grouped logicly while keeping LV performance in mind as I go (i.e. If I know I will be accessing an array of clusters based on one of the elements, I will copy that element off to a seperate array to facilitate quick searches, like a "key").
Once I have the data structures defined, I will then go through and design the code that will provide the functionality I need and take carefull note of who write the data objects and when.
It is durring this phase of the design that alarms will go off when I see that data needs to be modified from more than one place.
Each of the alarms are dealt with in turn and the conflicts are handled using the tools that come with LV (LV2 globals, queues, semaphores, etc).
So by the time I start coding I already know where my conflicts are and have plans in place to handle each.
I would be interested in hearing how others approach design.
LV is a unique dev environment that requires design techniques that are unique from other environments.
I hope this answers your question.
Ben