0

I'm trying to code a time out function that can work in a nested switch and case statement. However my problem is the following; the first switch/case works like expected, on the other hand, the second switch doesn't work in any way.

Here is my code:

#include <windows.h>
#include <iomanip>
#include <iostream>
#include <string>

namespace jsw {
    namespace threading {
        class auto_event {
        public:
            auto_event() : _event(CreateEvent(0, false, false, 0)) {}

            BOOL wait(DWORD timeout = 1) const
            {
                return WaitForSingleObject(_event, timeout) == WAIT_OBJECT_0;
            }

            BOOL set() { return SetEvent(_event); }
            BOOL reset_event() { return ResetEvent(_event); }
        private:
            HANDLE _event;
        };

        class thread {
        public:
            static thread start(
                LPTHREAD_START_ROUTINE fn, LPVOID args = 0,
                DWORD state = 0, DWORD timeout = 20000)
            {
                return thread(CreateThread(0, 0, fn, args, state, 0), timeout);
            }

            static void sleep(DWORD milliseconds) { Sleep(milliseconds); }
            static void exit(DWORD exitCode) { ExitThread(exitCode); }
        public:
            thread(HANDLE thread, DWORD timeout) : _thread(thread), _timeout(timeout) {}
            ~thread() { CloseHandle(_thread); }

            DWORD exit_code() const
            {
                DWORD exitCode = NO_ERROR;

                GetExitCodeThread(_thread, &exitCode);

                return exitCode;
            }

            HANDLE handle() const { return _thread; }
            BOOL is_alive() const { return exit_code() == STILL_ACTIVE; }
            DWORD join() { return WaitForSingleObject(_thread, _timeout); }
            DWORD suspend() { return SuspendThread(_thread); }
            DWORD resume() { return ResumeThread(_thread); }
            BOOL abort(DWORD exitCode) { return THREAD_TERMINATE; }
        private:
            HANDLE _thread;
            DWORD _timeout;
        };
    }
}

DWORD WINAPI get_option(LPVOID args)
{
    using namespace jsw::threading;

    int* option = (int*)((LPVOID*)args)[0];
    auto_event* e = (auto_event*)((LPVOID*)args)[1];

    std::cout << "Option: ";
    std::cin >> std::setw(1) >> *option;

    std::cin.clear();
    std::cin.ignore(INT_MAX, '\n');
    e->set();

    return NO_ERROR;
}

int main()
{
    using namespace jsw::threading;

    int option{};
    bool run{ true };
    auto_event e;
    LPVOID args[2] = { &option, &e };

    while (run)
    {
        thread worker = thread::start(get_option, args);

        if (e.wait(10000)) {
            system("cls");
            
            switch (option) {
            case 1:
                do {
                    std::cout << "Option: ";
                    std::cin >> option;
                    switch (option) {
                    case 1: std::cout << "Option 1 inside switch" << std::endl;
                        break;
                    case 2: std::cout << "Option 2 inside switch" << std::endl;
                        break;
                    case 3: std::cout << "Option 3 inside switch" << std::endl;
                        break;
                    case 4: 
                        break;
                    default: std::cout << "Option not valid!" << std::endl;
                        break;
                    }
                } while (option != 4);
                break;
            case 2:std::cout << "Option 2" << std::endl;
                break;
            case 3: run = false;
                break;
            default: std::cout << "Option not valid!" << std::endl;
                continue;
            }
        }
        else {
            worker.abort(NO_ERROR);
            system("cls");
            std::cout << "***PROGRAM END***\n";
            run = false;
        }
    }
}

I tried multiple things; like directly using cin, which does make the switch/case partially work, however, since I'm not going through the get_option function the timer doesn't work. If I try to use the do...while loop without cin then I get stuck in an infinite loop and if I get completely rid off the do...while loop (and cin) then I can't stay in the second switch/case, I always get sent back to the first one.

So if possible can somebody tell me how to fix this problem?

RexyCode
  • 21
  • 6
  • you are polling with two different thread on the same ressource "cin" ... can you describe in short (!!!) words, what you are trying to achieve, as this post does not really contain a very minimalistic example. – Synopsis Apr 06 '23 at 19:03
  • Note: Two threads fighting over a single IO stream is not a good idea. You need to protect it with critical sections, and that obliterates the ability to do two transactions at once. Side note: Killing threads should always be the last option. The odds of the program becoming unstable are too high. – user4581301 Apr 06 '23 at 19:03
  • Careful with suspend and resume. They are surprisingly hard to use correctly. As a result, you don't often find the ability to suspend and resume in threading libraries. The general recommendation is to use more easily managed synchronization primitives like semaphores. – user4581301 Apr 06 '23 at 19:08
  • @Synopsis In short, I'm trying to make the timer work inside the second `switch/case` statement and lastly I want to stay inside this second `switch/case`, that means not getting thrown back into the first switch statement when I input an option. – RexyCode Apr 06 '23 at 19:11
  • @user4581301 Probably should say that I only have 6 months of experience in programming so...yeah I still need to learn a lot. Do you know a website that could help me with what I'm trying to do? – RexyCode Apr 06 '23 at 19:14
  • I offer an XY alternative: the Standard IOstreams are too simple for what you want. They have to work exactly the same on Windows as they do on an embedded system where `cin` and `cout` are the programming front end for a UART. Instead use Windows-specific IO calls that can be safely interrupted by a timer. Not a Windows programmer so my suggestions will almost certainly be inferior. Instead go looking for a Stackoverflow question about how to interrupt an IO read on Windows. It'll probably have keywords like "Overlapped IO" or "WaitForMultipleObjects" – user4581301 Apr 06 '23 at 19:22
  • But hopefully one of the Windows gurus will be along shortly and get you pointed in the right direction. – user4581301 Apr 06 '23 at 19:23
  • @user4581301 Thank you for your input. Even though I have seen a bit how those functions work ("Overlapped IO" or "WaitForMultipleObjects"), the tough part is getting a good example that I can adapt for my program but I will try my best. Thanks again! – RexyCode Apr 06 '23 at 19:28
  • [A quick Google search popped this up](https://stackoverflow.com/a/19964096/4581301) It's a bit terse compared to the all-code answers voted higher, but Remy outlines how to do it and gives all of the functions you'll want to look up in Microsoft's documentation to write up exactly what you need. Side note: Don't roll your own thread. Start with [`std::thread`](https://en.cppreference.com/w/cpp/thread/thread). It's simple to use and as a result relatively hard to screw up. With `std::thread`, all of the mistakes usually come AFTER you get the thread running. – user4581301 Apr 06 '23 at 19:29
  • Look over the code answers in the link above and use Remy's description of what's going on and the keywords he provides to find documentation that'll help you understand how the other answers are working. The last thing you want to do is just copy the other answers and wind up stuck in a [Cargo Cult](https://en.wikipedia.org/wiki/Cargo_cult_programming). But the real beauty of this solution is it needs NO threads at all. – user4581301 Apr 06 '23 at 19:30
  • @user4581301 Thanks again, I will try to my best to make my program work with your input. – RexyCode Apr 06 '23 at 19:33

1 Answers1

-1

One cause for the above problem is the get_option itself where you specifically waiting for the user input in another thread leaving the main thread to continue executing, while the get_option func may return after even the execution of the switch cases, one possible is to use the sequentially design code Either using Mutex looks and make the main thread waits for the execution of the get_option thread Or by using another thread that is just called after the get_option thread where in the thread you store the switch cases and handle the functionality of your program

AmrShams07
  • 96
  • 1
  • 7
  • I think you're explaining the problem correctly but things got lost a bit in translation. Unfortunately this doesn't make an attempt to show how to solve the problem. In other words, this could make for a decent comment, but it's lacking as an answer. – user4581301 Apr 06 '23 at 19:49
  • Well, I mentioned two approaches – AmrShams07 Apr 06 '23 at 22:09
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 14 '23 at 16:42