In a general sense, you need some kind of multi-tasking, or an approximation of it. You need to have some code running "in the foreground", but all the time, you also need to have your alarm handling code doing its thing as well.
There are a number of ways to handle this. The best way depends on factors like the microcontroller you're using, the other tasks it needs to perform, and your level of experience.
It would help me to know more about your project - its general function, the other functions it performs, the microcontroller you're using, and anything else you can think of.
This is a general answer to the problem of getting periodic execution for a background task.
There are four basic approaches, which come from two independent choices you need to make.
The first is whether the backgound code (your alarm logic) will be executed co-operatively or pre-emptively - that is, whether the main code co-operates with the background code, and explicitly calls it regularly while the main code is idle, or whether the background code is triggered by an interrupt, usually from a timer. Co-operative is generally simpler to implement, especially for the less experienced, because it avoids a number of issues relating to interrupt handlers.
The second decision is whether the background code is implemented as a state-driven function, or as a task.
Using a task-based design is a good idea if there are many independent tasks that need to (appear to) run simultaneously. It requires a microcontroller that can switch between multiple stacks (small PICs can't), and it requires some overhead in firmware and stack space. It allows each task to be written as if it was the only program running on the microcontroller. The only requirement is that each task must co-operate with the other tasks, by "deferring" execution periodically to the other tasks, typically while it has nothing to do, at the start of its activity loop, and when it's doing something that might take a significant amount of time.
That is called co-operative multi-tasking. Alternatively, the deferring between tasks can be done through an interrupt; this is called pre-emptive multi-tasking and it brings in the complexities that are related to interrupt handlers.
Maintaining the watchdog can become a bit tricky with a task-based architecture. Usually you want the watchdog to time out when ANY task gets stuck, but there is usually only one hardware watchdog. Multiple watchdogs can be implemented in software, and the defer logic may be a good place to implement them, and to toggle the hardware watchdog if all the software watchdogs are being fed.
The alternative to a task-based architecture is a state-driven function. This is a function that is called periodically by the main code, in the same way that the defer() function is called in the task-switching architecture, but instead of deferring to other tasks, this function only performs the requirements of the alarm detection. This is what I recommend for your application.
The function needs to return quickly, so it can't implement any delays, but it knows that it's going to get executed regularly, so it doesn't need to tie up the microcontroller in a delay loop - it just needs to be able to remember what it needs to do until the next time it is called, and it needs some kind of independent time reference, such as a free-running counter, which nearly all microcontrollers have.
The function remembers its state through one or more variables, depending on what it needs to do. The traditional state-driven function has a single main state variable, and on entry, it uses a switch() to execute the section of code that is appropriate for its current state. That code will usually contain logic to switch to a different state in the right circumstances. After the case is executed, the function exits, and the next time it is called, a different state handler may execute.
A state-driven function usually has other variables that help it keep track of what it's doing. These variables may include sub-states for a particular state, counters, pointers, timestamps, and so on. Since a state-driven function is not allowed to do anything that waits for an event, or takes a significant amount of time, it should be impossible for the micro to get stuck in an endless loop inside the function. The function can still get stuck in a particular state though, due to a coding error or unexpected hardware behaviour; the function can include watchdog logic to watch for this situation (e.g. state != IDLE_STATE continuously for 1 second) and signal a failure.
Here's a summary of the four combinations of co-operative vs. pre-emptive and task-based vs. state-driven-function-based.
1. Co-operative task-based: All tasks, including the mainline, are written as if they are the only program running on the micro, but they must include regular calls to defer(), which transfers execution to the next due task; eventually, execution returns to the first task again. Tasks should not tie up the micro with long runs of code; typically, significant loops will include calls to defer(). There is overhead in the task-switching system, but each task can be written fairly easily without the need for state variables. There is no problem with interrupts because task switching is done co-operatively.
2. Pre-emptive task-based: All tasks, including the mainline, are written as if they are the only program running on the micro. No calls to defer() are needed, because deferring is forced through an interrupt. Special handshaking between tasks, using semaphores and queues, is usually needed to deal with the unpredictable timing of these interrupts, and code must be more carefully written.
3. Pre-emptive state-driven: This is another name for the traditional interrupt handler approach. The mainline doesn't defer. Periodically, an interrupt comes along, and the interrupt handler figures out "where it was up to", and what it needs to do, using one or more variables that identify the interrupt handler's current state, either directly or indirectly. The interrupt handler then takes the appropriate action, and may change its control variables, before exiting back to the mainline. If the interrupt handler is called at regular intervals from a timer interrupt, it can maintain a "tick counter" which it can use as a coarse time reference. Apart from in very simple cases, communication with the interrupt handler must be clearly defined and properly controlled to avoid race conditions and hard-to-debug failures.
4. Co-operative state-driven: This is what I recommend for this case. You have a function that handles the alarm logic, which must be called periodically by all mainline code - that is, at the top of the mainline (typically), inside any loop where the mainline is waiting for something to happen, and inside any loop where the mainline is doing something that may take a significant amount of time. This includes any functions called by the mainline - in fact, all the "foreground" code. The function is designed to be quick. It uses one or more variables to keep track of what it's doing, and an external timebase such as a free-running counter to keep track of the time, and on each call, it does the processing required to make decisions on what it needs to do, it updates hardware outputs if appropriate, it updates its own variables, then it returns to the mainline.
That's my attempt at "Multi-tasking 101". Get back to us with your response to my suggestions, and other information about the project, and I'll be able to help some more.