Each task can be thought of as a logically complete and self-contained program segment and includes such elements as the function that executes when the task is running, a string name, and a priority that as was previously mentioned, can change during runtime. Each task can also have an environment pointer that basically allows a unique data structure to be tied to that task instance. One essential concept to understand about tasks is that each task instance has its own stack. This is what allows tasks to block and differentiates them from software interrupt threads, which by definition must always run to completion. The fact that each task must allocate its own stack makes them more functional and easier to use, but also makes them hungry in terms of memory usage, so they may be inappropriate for use in systems with highly constrained memory. Note that in the diagram shown here, both tasks are pointing to the same function to execute. This is perfectly acceptable (although not required) as long as the function “reentrant”, meaning that it can it can be safely called again before its previous invocation has been completed. Tasks can call the function with arguments, which could allow the function in essence to “know” which task instance it was called from.