Disclaimer: I'm quite certain that this has been answered somewhere, but myself and another person have been searching quite hard to no avail.
I've got a code that looks something like this:
PROGRAM main
!$omp parallel do
!$omp private(somestuff) shared(otherstuff)
DO i=1,n
...
CALL mysubroutine(args)
...
a=myfunction(moreargs)
...
ENDDO
!$omp end parallel do
END PROGRAM
SUBROUTINE mysubroutine(things)
...
END SUBROUTINE
FUNCTION myfunction(morethings)
...
END FUNCTION
I cannot determine where/how to handle private, shared, reduction, etc. clauses for the variables in the subroutine and function. I suspect there may be some nuances to the answer, as there are many, many ways variables might have been declared and shared amongst them. So, let's say all variables that the main program are concerned with were defined in it or in shared modules, and that any OMP operations on those variables can be handled in the main code. The subroutines and functions use some of those variables, and have some of their own variables. So, I think the question boils down to how to handle clauses for their local variables.
OK, this is about the difference between the lexical and dynamic extent of OpenMP directives and the interaction with variable scoping. The lexical extent of a directive is the text between the beginning and the end of the structured block following a directive. The dynamic extent is the lexical extent plus statements executed as part of any subprogram executed as a result of statements in the lexical extent. So in something like
(totally untested, written direct in here) the lexical extent of the parallel region initiated by !$omp parallel is just
while the dynamic extent is the call and the contents of the subroutine. And for completeness of terminology the !$omp do is an example of an orphaned directive, a directive that is not in the lexical extent of another directive, but in the dynamic extent. See
https://computing.llnl.gov/tutorials/openMP/#Scoping
for another example.
Why does this matter? Well you can only explicitly scope variables for entities in the lexical scope, just the array a in this case, but for entities that become defined due to the dynamic extent being executed you can't do this! Instead OpenMP has a number of rules, which in simple terms are
And in my experience that gets you most of the way! Use of the dynamic extent combined with orphaned directives is a great way to keep an OpenMP program under control, and I have to say I disagree with the above comment, I find orphaned workshare directives very useful indeed! So you can combine all of the above to do things like
This simple situation is a bit complicated by module variables and common blocks, so don't use global variables ... but if you must they are by default shared unless declared as threadprivate. See
https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE
for an example.