Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Apr 13, 2013, 10:42 PM   #1
ravenvii
macrumors 604
 
ravenvii's Avatar
 
Join Date: Mar 2004
Location: Melenkurion Skyweir
[C] Watch input for quit command WITHOUT interfering with the program?

Before I begin -- YES, this is via a homework assignment. However, the functionality I'm asking about is beyond the scope of the homework itself.

This might be a strange question -- and if my thinking is going down the wrong alley, let me know! -- but I'm looking for a way to watch the input for a quit command WITHOUT interfering with the program.

The program is a simple CLI socket-based server-client chat program using multithreading. I got all of it working great in the scope of the assignment.

However, it bothers me that the program cannot quit gracefully -- I have to hit Ctrl-C to quit it. So I went to work on getting it to quit gracefully. I've managed to make it detect if the server or client is no longer running, and quit.

However, that still requires me to Ctrl-C the *other* program.

The obvious step would be a simple "if input equals "/quit" then return 0" and fork it off into it's own infinite loop.

Obvious problem: this is a chat program, so input is being monitored for read/write via socket. Before you ask: I've tried putting the if-statement just before the write() call, but this doesn't work because even though it terminates the writing thread, it doesn't terminate the reading thread.

So is there a way to fork main and have that "passively" watch input, and execute as soon as it sees a matching input?

This needs to be without blocking, obviously.

I'm probably over-thinking this, but I figured this would be a good learning experience, even though it's beyond the scope of the assignment itself.

Whew, sorry for the lengthy post!

TL;DR: How to passively monitor input in CLI without blocking, for a matching input that would exit the program?
__________________
59 6F 75 20 73 70 6F 6F 6E 79 20 62 61 72 64 21
ravenvii is offline   0 Reply With Quote
Old Apr 14, 2013, 12:42 PM   #2
chown33
macrumors 603
 
Join Date: Aug 2009
1. What language are you working in?

2. What libraries are you working with? Standard C? Posix? C++? Objective-C?

3. Where do you want the "quit command" request to come from? The keyboard? Remote? Somewhere else, like a signal via kill(2) or sigaction(2)? The origin may seem clear to you because you're involved in the program from its inception, but it's not entirely clear to me, even after reading your entire post a few times.

4. How do you read the sendable "chat" text now? Which function? What happens after reading it? Are you reading char-by-char or line-by-line? Doing any post-processing before sending? Post your code.

5. Is it a point-to-point chat program (exactly 2 endpoints), or a group chat (multiple endpoints)? I've seen both kinds called "chat programs", so it's unclear what you have.


Quote:
Obvious problem: this is a chat program, so input is being monitored for read/write via socket. Before you ask: I've tried putting the if-statement just before the write() call, but this doesn't work because even though it terminates the writing thread, it doesn't terminate the reading thread.
Without code, this means nothing.

A point-to-point chat program has at least two inputs, one being the local keyboard, the other being the remote connection (presumably a socket). So when you say "input is being monitored for read/write via socket", it sounds like you want the QUIT command to come from the remote connection, i.e. the remote chat participant. That doesn't make a lot of sense to me.

A group chat program has multiple inputs, each one being remote, and no local keyboard input (typically). It's essentially a faceless daemon, listening for and accepting any number of connections, and mirroring any input onto all connected output sockets.

If your design doesn't fall into one of those areas, then please explain exactly what your program does.

And post your code, so we can see what the threads are doing, and where this write() call is.
chown33 is offline   0 Reply With Quote
Old Apr 14, 2013, 01:08 PM   #3
ravenvii
Thread Starter
macrumors 604
 
ravenvii's Avatar
 
Join Date: Mar 2004
Location: Melenkurion Skyweir
Quote:
Originally Posted by chown33 View Post
1. What language are you working in?

[...]

And post your code, so we can see what the threads are doing, and where this write() call is.
I'm writing in C.

write() is just a function in unistd.h

It's just a simple server-to-client chat program using the command line. Input (at both ends) is via keyboard using fgets.
__________________
59 6F 75 20 73 70 6F 6F 6E 79 20 62 61 72 64 21

Last edited by ravenvii; Apr 16, 2013 at 01:18 PM.
ravenvii is offline   0 Reply With Quote
Old Apr 14, 2013, 01:36 PM   #4
chown33
macrumors 603
 
Join Date: Aug 2009
Quote:
Originally Posted by ravenvii View Post
Code:
...
void *sending(void *arg) {
	char output[100];	

	while(1) {
		bzero(output, 100);

		fgets(output, 100, stdin);
		if(write(sockfd, output, strlen(output) + 1) == -1)
			return NULL;
	}
}
...
The simplest place to look for a "/quit" command is between the fgets() and the write(). If it sees a "/quit" in the buffer, it should terminate the thread without sending the "/quit".

Or since your main() isn't doing anything else useful, it can run the sending() function directly, instead of spawning a separate thread for it. Then when "/quit" appears in the buffer, sending() just returns.

It's not necessary to terminate the reading() thread. Simply exit the program and all threads will terminate.

There are other ways of doing it, such as having a thread-safe condition variable in each thread's while loop. I.e. where you have while(1) use while(threadSafeCondition) instead, then have the detection of "/quit" clear the condition variable.

You can also look for EOF on stdin after fgets(), rather than an overt "/quit" command. This is left as an exercise for the reader.
chown33 is offline   0 Reply With Quote
Old Apr 14, 2013, 02:49 PM   #5
ravenvii
Thread Starter
macrumors 604
 
ravenvii's Avatar
 
Join Date: Mar 2004
Location: Melenkurion Skyweir
Quote:
Originally Posted by chown33 View Post
The simplest place to look for a "/quit" command is between the fgets() and the write(). If it sees a "/quit" in the buffer, it should terminate the thread without sending the "/quit".
I did that, but it didn't terminate the receiving thread. I didn't think of simply removing pthread_join for the receiving thread. That'll work.

(Side-question: Should I just let main return, or should I explicitly put in pthread_cancel() before returning? Which is the better practice?)

Quote:
Or since your main() isn't doing anything else useful, it can run the sending() function directly, instead of spawning a separate thread for it. Then when "/quit" appears in the buffer, sending() just returns.
I thought of this as well, but as this is homework, the instructor requires that both sending + receiving be threads, so that's that

Quote:
There are other ways of doing it, such as having a thread-safe condition variable in each thread's while loop. I.e. where you have while(1) use while(threadSafeCondition) instead, then have the detection of "/quit" clear the condition variable.
I'm in the middle of doing just this actually -- a global int set to 0 which changes to 1 if /quit is detected. This terminates both the sending and receiving loops. However, it doesn't work very well because the program ends up waiting for the loop to terminate before noticing

(And I'm trying to minimize the use of global variables -- they're bad practice... right?)

Thanks for the help/input!
__________________
59 6F 75 20 73 70 6F 6F 6E 79 20 62 61 72 64 21
ravenvii is offline   0 Reply With Quote
Old Apr 14, 2013, 03:12 PM   #6
chown33
macrumors 603
 
Join Date: Aug 2009
Quote:
Originally Posted by ravenvii View Post
I did that, but it didn't terminate the receiving thread. I didn't think of simply removing pthread_join for the receiving thread. That'll work.

(Side-question: Should I just let main return, or should I explicitly put in pthread_cancel() before returning? Which is the better practice?)
It depends on the scope of the design. This is a simplified homework design, not a design intended for reuse or even real-world deployment. I would choose simplicity, and recognize that the inevitable exit will terminate all threads anyway. If you feel the need to demonstrate that knowledge, put it in a comment.


Quote:
I'm in the middle of doing just this actually -- a global int set to 0 which changes to 1 if /quit is detected. This terminates both the sending and receiving loops. However, it doesn't work very well because the program ends up waiting for the loop to terminate before noticing
Post your code. If you want someone to comment on code or make suggestions, you have to post it. Otherwise all we can do is guess what you actually wrote.


Quote:
(And I'm trying to minimize the use of global variables -- they're bad practice... right?)
Again, it depends on the design. Excessive coupling can be bad practice. There may be perfectly sound reasons for globals, but if there's no reason, then there's no reason.

What's the absolute smallest variable scope that will work?

Clearly, it doesn't need to be global (visible in other compilation units), because both thread functions are in a single file. What's the C keyword that limits visibility to a single compilation unit, while also making it visible to the entire compilation unit?

Then what's the C keyword that ensures reads/writes will be suitably thread-safe (with the minimal amount of thread-safety)?


Answers (in white text, so select and copy/paste to read)
[ static volatile ]

Last edited by chown33; Apr 14, 2013 at 03:17 PM. Reason: typos
chown33 is offline   0 Reply With Quote
Old Apr 15, 2013, 05:13 AM   #7
cqexbesd
macrumors member
 
Join Date: Jun 2009
Quote:
Originally Posted by ravenvii View Post
I'm in the middle of doing just this actually -- a global int set to 0 which changes to 1 if /quit is detected. This terminates both the sending and receiving loops. However, it doesn't work very well because the program ends up waiting for the loop to terminate before noticing
You need to use some form of asynchronous notification to do this well. Probably the easiest is to use pthread_kill to send a signal to a particular thread when you want it to exit. Make sure the thread tests the return value of all the functions it calls. If one fails and errno is set to EINTR then you know to check some form of flag (e.g. a global variable) to see if its time to exit. You still need to set up your signal handlers and the like. It takes a lot of thought to get this right in all but the trivialest of cases to avoid race conditions or long delays in processing the quit.

It may not be worth the trouble in your case. Usually if I write a moderately complex threaded app I will dedicate one thread to housekeeping (usually the main thread). It receives signals aimed at he whole process (e.g. TERM, HUP) or notifications that its time to exit and is responsible for letting everyone know and trying to shut things down gracefully. It can be one of the more complex tasks in the whole program sometimes, esp on fatal error conditions.

The alternative is to be single threaded and use select/kqueue/poll etc to multiplex the different streams. For simple tasks this can be much easier to work with.
cqexbesd is offline   0 Reply With Quote
Old Apr 16, 2013, 01:17 PM   #8
ravenvii
Thread Starter
macrumors 604
 
ravenvii's Avatar
 
Join Date: Mar 2004
Location: Melenkurion Skyweir
I ended up just dumping the disconnect detection and leave '/quit' as the only way to exit the program.

Covers much more use-cases, and guarantees a way to shut down both the server and client.

Thanks guys!
__________________
59 6F 75 20 73 70 6F 6F 6E 79 20 62 61 72 64 21
ravenvii is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
Flatscreen interfering w/ trackpad? tweak25 MacBook Pro 0 Jan 12, 2014 11:45 AM
Auto-Quit Program with Automator ReDDMax OS X 0 Nov 17, 2013 07:18 PM
When I quit a program, menubar disappears? Ploki OS X 10 Jun 7, 2013 06:48 PM
App or Program that will Allow You To Watch iTunes Password Protected Movies ? Inframan Mac Applications and Mac App Store 1 Feb 18, 2013 02:52 PM
Command to open other programs when opening another program ISJB OS X 0 Jun 27, 2012 11:28 AM

Forum Jump

All times are GMT -5. The time now is 11:33 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC