Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Timed Input Delivery Methods for Multi-Threaded Interactive Console Applications

Tech 1

Manual Console Input

Overview

Directly type interactive elevator requests into the terminal during program execution.

Implementation

N/A.

Tradeoffs

Pros: Zero setup, no external tools required. Cons: Impossible to time precisely, inefficient for large test suites, and typing gets disrupted by interleaved program stdout. Recommendation: Not recommended for structured testing.

Timed Output Generator + Unix/Windows Pipeline

Overview

Create a lightweight input scheduler that parses timestamped test data and emits lines at specified offsets, then pipe its output directly into the target applicasion.

Implementation

Scheduler Program

This tool reads timestamped input lines either from stdin or a file, waits the required duration from the last emission, and flushes each line immediately to avoid buffering delays.

// timed_sender.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#ifdef _WIN32
#include <windows.h>
#define sleep_ms(ms) Sleep((DWORD)(ms))
#else
#include <unistd.h>
#define sleep_ms(ms) usleep((useconds_t)(ms) * 1000)
#endif

#define MAX_LINE_LENGTH 1024

int main(void) {
    char line_buffer[MAX_LINE_LENGTH];
    long next_offset_ms;
    long last_offset = 0;

    while (fgets(line_buffer, MAX_LINE_LENGTH, stdin) != NULL) {
        char *content_ptr;
        next_offset_ms = strtol(line_buffer, &content_ptr, 10);
        if (*content_ptr == ':') {
            content_ptr++;
        }
        long wait_ms = next_offset_ms - last_offset;
        if (wait_ms > 0) {
            sleep_ms(wait_ms);
        }
        fputs(content_ptr, stdout);
        fflush(stdout);
        last_offset = next_offset_ms;
    }

    return 0;
}

Test Input Format

Each line starts with an integer offset in milliseconds from program start, followed by a colon and the actual input line. Example:

0:Initialize
2000:Elevator request 1-5
3200:Elevator request 3-2
5500:Shutdown

Execution Command

Compile the scheduler, prepare a test file, and pipe them together:

# Linux/macOS
./timed_sender < test_cases.txt | java -jar elevator_system.jar

# Windows Command Prompt
timed_sender.exe < test_cases.txt | java -jar elevator_system.jar

Tradeoffs

Pros: Fully scriptable, ideal for automated regression and batch testing. Cons: Cannot use IDE breakpoints easily, requires a precompiled JAR or separate CLI setup. Recommendation: Use for continuous local testing or peer review automation.

Key Notes

Always call fflush(stdout) immediately after writing each line to prevent pipeline buffering, which would delay all inputs until the scheduler exits. On Unix-like systems, use ./ to execute the compiled binary if it’s not in you're PATH.

Java Timer + Piped Streams

Overview

Embed a debug input handler directly into your Java application using Timer for scheduling and PipedInputStream/PipedOutputStream to mimic standard input without leaving the IDE.

Implementation

Core Concepts

  • Timer & TimerTask: Schedule input lines to be written at precise timestamps relative to program launch.
  • Piped Streams: Create a thread-safe communication channel between your debug handler and the official input class; PipedOutputStream writes data, PipedInputStream reads it.

Step-by-Step Setup

  1. Initialize a connected pair of piped streams:
import java.io.*;

public class Main {
    private static final boolean USE_DEBUG_INPUT = true; // Toggle before submission!

    public static void main(String[] args) throws IOException {
        PipedInputStream debugIn = new PipedInputStream();
        PipedOutputStream debugOut = new PipedOutputStream(debugIn);

        ElevatorInput inputSource;
        if (USE_DEBUG_INPUT) {
            inputSource = new ElevatorInput(debugIn);
            new Thread(new DebugInputFeeder(debugOut)).start();
        } else {
            inputSource = new ElevatorInput(System.in);
        }

        // Start main application threads with inputSource
    }
}
  1. Create the DebugInputFeeder class to parse timestamped input and schedule writes:
import java.util.*;
import java.util.regex.*;

public class DebugInputFeeder implements Runnable {
    private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("^\\[(\\d+(?:\\.\\d+)?)\\](.*)$");
    private final OutputStream targetStream;

    public DebugInputFeeder(OutputStream stream) {
        this.targetStream = stream;
    }

    @Override
    public void run() {
        Timer scheduler = new Timer("Debug-Input-Timer", true);
        long programStart = System.currentTimeMillis();
        List<TimerTask> cleanupTasks = new ArrayList<>();

        try (Scanner fileScanner = new Scanner(System.in)) {
            while (fileScanner.hasNextLine()) {
                String rawLine = fileScanner.nextLine().trim();
                if (rawLine.isEmpty()) continue;

                Matcher matcher = TIMESTAMP_PATTERN.matcher(rawLine);
                if (!matcher.matches()) continue;

                double offsetSec = Double.parseDouble(matcher.group(1));
                long offsetMs = (long) (offsetSec * 1000);
                String payload = matcher.group(2).trim() + "\n";

                TimerTask writeTask = new TimerTask() {
                    @Override
                    public void run() {
                        try {
                            targetStream.write(payload.getBytes());
                            targetStream.flush();
                        } catch (IOException e) {
                            throw new UncheckedIOException(e);
                        }
                    }
                };
                scheduler.schedule(writeTask, new Date(programStart + offsetMs));
                cleanupTasks.add(writeTask);
            }
        }

        // Schedule cleanup after the last payload
        if (!cleanupTasks.isEmpty()) {
            TimerTask lastTask = cleanupTasks.get(cleanupTasks.size() - 1);
            scheduler.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        targetStream.close();
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                    scheduler.cancel();
                }
            }, new Date(programStart + 2000)); // Add extra buffer
        }
    }
}
  1. Configure IDE input redirection (e.g., IntelliJ IDEA: Run → Edit Configurations → Modify options → Redirect input from file).

Test Input Format (Regex-Enhanced)

[0.0]InitializeSystem
[1.2]1-FROM-1-TO-10
[2.7]2-FROM-5-TO-2
[4.0]Terminate

Tradeoffs

Pros: Full IDE debugging suport, works with both direct file input and stdin, no external binaries needed. Cons: Requires adding debug-only code to your project (toggle the switch off before submission!). Recommendation: Ideal for iterative development and breakpoint debugging.

Tags: Java

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.