<commit_before>package com.ra4king.circuitsim.simulator.components.wiring;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.ra4king.circuitsim.simulator.Circuit;
import com.ra4king.circuitsim.simulator.CircuitState;
import com.ra4king.circuitsim.simulator.Component;
import com.ra4king.circuitsim.simulator.Simulator;
import com.ra4king.circuitsim.simulator.Utils;
import com.ra4king.circuitsim.simulator.WireValue;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;

/**
 * @author <NAME>
 */
public class Clock extends Component {
	public static class EnabledInfo {
		private final boolean enabled;
		private final int hertz;
		
		public EnabledInfo(boolean enabled, int hertz) {
			this.enabled = enabled;
			this.hertz = hertz;
		}
		
		public boolean getEnabled() {
			return enabled;
		}
		
		public int getHertz() {
			return hertz;
		}
		
		@Override
		public boolean equals(Object o) {
			if (o == this) {
				return true;
			}
			
			if (!(o instanceof EnabledInfo)) {
				return false;
			}
			
			EnabledInfo e = (EnabledInfo) o;
			
			return enabled == e.enabled && hertz == e.hertz;
		}
		
		@Override
		public int hashCode() {
			return Objects.hash(enabled, hertz);
		}
	}
	
	private static class ClockInfo {
		private final Map<Clock, Object> clocks = new ConcurrentHashMap<>();
		private final Map<ClockChangeListener, Object> clockChangeListeners = new ConcurrentHashMap<>();
		
		private Thread currentClock;
		private final SimpleObjectProperty<Clock.EnabledInfo> clockEnabled = new SimpleObjectProperty<>(
				new Clock.EnabledInfo(false, 0));
		private boolean clock;
		
		private long lastTickTime;
		private long lastPrintTime;
		private int tickCount;
		private volatile int lastTickCount;
		
		private ClockInfo() {
			clockEnabled.addListener((obs, oldValue, newValue) -> {
				if(newValue.enabled) {
					startClock(newValue.getHertz());
				} else {
					stopClock(false);
				}
			});
		}
		
		void reset() {
			stopClock(true);
			if (clock) {
				tick();
			}
		}
		
		void tick() {
			clock = !clock;
			WireValue clockValue = WireValue.of(clock ? 1 : 0, 1);
			clocks.forEach((clock, o) -> {
				if (clock.getCircuit() != null) {
					clock.getCircuit().forEachState(state -> state.pushValue(clock.getPort(PORT), clockValue));
				}
			});
			clockChangeListeners.forEach((listener, o) -> listener.valueChanged(clockValue));
		}
		
		void startClock(int hertz) {
			lastTickTime = lastPrintTime = System.nanoTime();
			lastTickCount = tickCount = 0;
			
			final long nanosPerTick = (long)(1e9 / (2L * hertz));
			
			stopClock(true);
			Thread clockThread = new Thread(() -> {
				Thread thread = currentClock;
				
				while (thread != null && !thread.isInterrupted()) {
					long now = System.nanoTime();
					if (now - lastPrintTime >= 1e9) {
						lastTickCount = tickCount;
						tickCount = 0;
						lastPrintTime = now;
						lastTickTime = now;
					}
					
					tick();
					tickCount++;
					
					lastTickTime += nanosPerTick;
					
					long diff = lastTickTime - System.nanoTime();
					if (diff >= 1e6 || (tickCount >> 1) >= hertz) {
						try {
							Thread.sleep(Math.max(1, (long)(diff / 1e6)));
						} catch (InterruptedException exc) {
							break;
						}
					}
				}
			});
			
			clockThread.setName("Clock thread");
			clockThread.setDaemon(true);
			
			currentClock = clockThread;
			clockThread.start();
		}
		
		void stopClock(boolean shouldYield) {
			if (currentClock != null) {
				Thread clockThread = currentClock;
				
				currentClock.interrupt();
				currentClock = null;
				
				while (clockThread.isAlive() && shouldYield) {
					Thread.yield();
				}
				
				lastTickCount = 0;
			}<commit_msg>Use an AtomicBoolean instead of the interrupt signal to control whether the clock thread is enabled. Also check whether we're in the clock thread to decide whether to wait for it to stop in stopClock().<commit_after>package com.ra4king.circuitsim.simulator.components.wiring;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import com.ra4king.circuitsim.simulator.Circuit;
import com.ra4king.circuitsim.simulator.CircuitState;
import com.ra4king.circuitsim.simulator.Component;
import com.ra4king.circuitsim.simulator.Simulator;
import com.ra4king.circuitsim.simulator.Utils;
import com.ra4king.circuitsim.simulator.WireValue;

import javafx.beans.property.SimpleObjectProperty;

/**
 * @author <NAME>
 */
public class Clock extends Component {
	public static class EnabledInfo {
		private final boolean enabled;
		private final int hertz;
		
		public EnabledInfo(boolean enabled, int hertz) {
			this.enabled = enabled;
			this.hertz = hertz;
		}
		
		public boolean getEnabled() {
			return enabled;
		}
		
		public int getHertz() {
			return hertz;
		}
		
		@Override
		public boolean equals(Object o) {
			if (o == this) {
				return true;
			}
			
			if (!(o instanceof EnabledInfo)) {
				return false;
			}
			
			EnabledInfo e = (EnabledInfo)o;
			
			return enabled == e.enabled && hertz == e.hertz;
		}
		
		@Override
		public int hashCode() {
			return Objects.hash(enabled, hertz);
		}
	}
	
	private static class ClockInfo {
		private final Map<Clock, Object> clocks = new ConcurrentHashMap<>();
		private final Map<ClockChangeListener, Object> clockChangeListeners = new ConcurrentHashMap<>();
		
		private static class InternalClockInfo {
			private final Thread thread;
			private final AtomicBoolean enabled = new AtomicBoolean(true);
			
			InternalClockInfo(Thread thread) {
				this.thread = thread;
			}
		}
		
		private InternalClockInfo currentClock;
		private final SimpleObjectProperty<Clock.EnabledInfo>
			clockEnabled
			= new SimpleObjectProperty<>(new Clock.EnabledInfo(false, 0));
		private boolean clock;
		
		private long lastTickTime;
		private long lastPrintTime;
		private int tickCount;
		private volatile int lastTickCount;
		
		private ClockInfo() {
			clockEnabled.addListener((obs, oldValue, newValue) -> {
				if (newValue.enabled) {
					startClock(newValue.getHertz());
				} else {
					stopClock();
				}
			});
		}
		
		void reset() {
			stopClock();
			if (clock) {
				tick();
			}
		}
		
		synchronized void tick() {
			clock = !clock;
			WireValue clockValue = WireValue.of(clock ? 1 : 0, 1);
			clocks.forEach((clock, o) -> {
				if (clock.getCircuit() != null) {
					clock.getCircuit().forEachState(state -> state.pushValue(clock.getPort(PORT), clockValue));
				}
			});
			clockChangeListeners.forEach((listener, o) -> listener.valueChanged(clockValue));
		}
		
		synchronized void startClock(int hertz) {
			lastTickTime = lastPrintTime = System.nanoTime();
			lastTickCount = tickCount = 0;
			
			final long nanosPerTick = (long)(1e9 / (2L * hertz));
			
			stopClock();
			Thread clockThread = new Thread(() -> {
				InternalClockInfo currentClock = this.currentClock;
				if (currentClock == null || !Thread.currentThread().equals(currentClock.thread)) {
					return;
				}
				
				while (currentClock.enabled.get()) {
					long now = System.nanoTime();
					if (now - lastPrintTime >= 1e9) {
						lastTickCount = tickCount;
						tickCount = 0;
						lastPrintTime = now;
						lastTickTime = now;
					}
					
					tick();
					tickCount++;
					
					lastTickTime += nanosPerTick;
					
					long diff = lastTickTime - System.nanoTime();
					if (diff >= 1e6 || (tickCount >> 1) >= hertz) {
						try {
							Thread.sleep(Math.max(1, (long)(diff / 1e6)));
						} catch (InterruptedException exc) {
							break;
						}
					}
				}
			});
			
			clockThread.setName("Clock thread");
			clockThread.setDaemon(true);
			
			currentClock = new InternalClockInfo(clockThread);
			clockThread.start();
		}
		
		synchronized void stopClock() {
			if (currentClock != null) {
				InternalClockInfo currentClock = this.currentClock;
				
				currentClock.enabled.set(false);
				this.currentClock = null;
				
				boolean isClockThread = Thread.currentThread().equals(currentClock.thread);
				// Wait for the clock thread to die if we're not already in the clock thread.
				while (currentClock.thread.isAlive() && !isClockThread) {
					Thread.yield();
				}
				
				lastTickCount = 0;
			}
{'fraction_non_alphanumeric': 0.11391954432182272, 'fraction_numerical': 0.005933309600094933, 'mean_word_length': 3.178482895389192, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4335341', 'n_tokens_mistral': 3327, 'n_tokens_neox': 2555, 'n_words': 607}