<commit_before>/*
 * Copyright (c) 2011, <NAME> / SpareTimeLabs
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list 
 * of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice, this 
 * list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 *  
 * Neither the name of the Kustaa Nyholm or SpareTimeLabs nor the names of its 
 * contributors may be used to endorse or promote products derived from this software 
 * without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jtermios.windows;

import java.io.File;
import java.nio.ByteBuffer;

import java.util.*;
import java.util.regex.Pattern;

import com.sun.jna.*;
import com.sun.jna.ptr.IntByReference;

import static jtermios.JTermios.*;
import static jtermios.JTermios.JTermiosLogging.*;
import jtermios.*;
import jtermios.windows.WinAPI.*;
import static jtermios.windows.WinAPI.*;
import static jtermios.windows.WinAPI.DCB.*;

public class JTermiosImpl implements jtermios.JTermios.JTermiosInterface {
	private volatile int m_ErrNo = 0;

	private volatile boolean m_PortFDs[] = new boolean[FDSetImpl.FD_SET_SIZE];

	private volatile Hashtable<Integer, Port> m_OpenPorts = new Hashtable<Integer, Port>();

	private class Port {
		volatile int m_FD = -1;
		volatile boolean m_Locked;
		volatile HANDLE m_Comm;
		volatile int m_OpenFlags;
		volatile DCB m_DCB = new DCB();
		volatile COMMTIMEOUTS m_Timeouts = new COMMTIMEOUTS();
		volatile COMSTAT m_COMSTAT = new COMSTAT();
		volatile int[] m_ClearErr = { 0 };
		volatile Memory m_RdBuffer = new Memory(2048);
		volatile int[] m_RdErr = { 0 };
		volatile int m_RdN[] = { 0 };
		volatile OVERLAPPED m_RdOVL = new OVERLAPPED();
		volatile Memory m_WrBuffer = new Memory(2048);
		volatile COMSTAT m_WrStat = new COMSTAT();
		volatile int[] m_WrErr = { 0 };
		volatile int m_WrN[] = { 0 };
		volatile int m_WritePending;
		volatile OVERLAPPED m_WrOVL = new OVERLAPPED();
		volatile int m_SelN[] = { 0 };
		volatile HANDLE m_CancelWaitSema4;
		volatile OVERLAPPED m_SelOVL = new OVERLAPPED();
		volatile IntByReference m_EventFlags = new IntByReference();
		volatile Termios m_Termios = new Termios();
		volatile int MSR; // initial value
		// these cached values are used to detect changes in termios structure and speed up things by avoiding unnecessary updates
		volatile int m_VTIME = -1;
		volatile int m_VMIN = -1;
		volatile int m_c_speed = -1;
		volatile int m_c_cflag = -1;
		volatile int m_c_iflag = -1;
		volatile int m_c_oflag = -1;

		synchronized public void fail() throws Fail {
			int err = GetLastError();
			Memory buffer = new Memory(2048);
			int res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, (int) buffer.size(), null);

			log = log && log(1, "fail() %s, Windows GetLastError()= %d, %s\n", lineno(1), err, buffer.getString(0, true));

			// FIXME here convert from Windows error code to 'posix' error code

			Fail f = new Fail();
			throw f;
		}

		synchronized public void lock() throws InterruptedException {
			if (m_Locked)
				wait();
			m_Locked = true;
		}

		synchronized public void unlock() {
			if (!m_Locked)
				throw new IllegalArgumentException("Port was not locked");
			m_Locked = false;
			notifyAll();
		}

		synchronized public void waitUnlock() {
			while (m_Locked) {
				try {
					wait();
				} catch (InterruptedException e) {
					// interruption cannot cancel wait
				}
			}
		}

		public Port() {
			synchronized (JTermiosImpl.this) {
				m_FD = -1;
				for (int i = 0; i < m_PortFDs.length; ++i) {
					if (!m_PortFDs[i]) {
						m_FD = i;
						m_PortFDs[i] = true;
						m_OpenPorts.put(m_FD, this);
						m_CancelWaitSema4 = CreateEventA(null, false, false, null);
						if (m_CancelWaitSema4 == null)
							throw new RuntimeException("Unexpected failure of CreateEvent() call");
						return;
					}
				}
				throw new RuntimeException("Too many ports open");
			}
		}

		public void close() {
			synchronized (JTermiosImpl.this) {
				if (m_FD >= 0) {
					m_OpenPorts.remove(m_FD);
					m_PortFDs[m_FD] = false;
					m_FD = -1;
				}

				if (m_CancelWaitSema4 != null)
					SetEvent(m_CancelWaitSema4);
				if (m_Comm != null) {
					ResetEvent(m_SelOVL.hEvent);

					if (!CancelIo(m_Comm))
						log = log && log(1, "CancelIo() failed, GetLastError()= %d, %s\n", GetLastError(), lineno(1));
					if (!PurgeComm(m_Comm, PURGE_TXABORT + PURGE_TXCLEAR + PURGE_RXABORT + PURGE_RXCLEAR))
						log = log && log(1, "PurgeComm() failed, GetLastError()= %d, %s\n", GetLastError(), lineno(1));
				}
				HANDLE h; // / 'hEvent' might never have been 'read' so read it
				// to this var first

				synchronized (m_RdBuffer) {
					h = (HANDLE) m_RdOVL.readField("hEvent");
					m_RdOVL = null;
					if (h != null && !h.equals(NULL) && !h.equals(INVALID_HANDLE_VALUE))
						CloseHandle(h);
				}

				synchronized (m_WrBuffer) {
					h = (HANDLE) m_WrOVL.readField("hEvent");
					m_WrOVL = null;

					if (h != null && !h.equals(NULL) && !h.equals(INVALID_HANDLE_VALUE))
						CloseHandle(h);
				}

				// Ensure that select() is through before releasing the m_SelOVL<commit_msg>After calling CancelIo, wait for all IO operations to finish before closing handles and freeing OVERLAPPED structures.<commit_after>/*
 * Copyright (c) 2011, <NAME> / SpareTimeLabs
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list 
 * of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice, this 
 * list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 *  
 * Neither the name of the <NAME> or SpareTimeLabs nor the names of its 
 * contributors may be used to endorse or promote products derived from this software 
 * without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jtermios.windows;

import java.io.File;
import java.nio.ByteBuffer;

import java.util.*;
import java.util.regex.Pattern;

import com.sun.jna.*;
import com.sun.jna.ptr.IntByReference;

import static jtermios.JTermios.*;
import static jtermios.JTermios.JTermiosLogging.*;
import jtermios.*;
import jtermios.windows.WinAPI.*;
import static jtermios.windows.WinAPI.*;
import static jtermios.windows.WinAPI.DCB.*;

public class JTermiosImpl implements jtermios.JTermios.JTermiosInterface {
	private volatile int m_ErrNo = 0;

	private volatile boolean m_PortFDs[] = new boolean[FDSetImpl.FD_SET_SIZE];

	private volatile Hashtable<Integer, Port> m_OpenPorts = new Hashtable<Integer, Port>();

	private class Port {
		volatile int m_FD = -1;
		volatile boolean m_Locked;
		volatile HANDLE m_Comm;
		volatile int m_OpenFlags;
		volatile DCB m_DCB = new DCB();
		volatile COMMTIMEOUTS m_Timeouts = new COMMTIMEOUTS();
		volatile COMSTAT m_COMSTAT = new COMSTAT();
		volatile int[] m_ClearErr = { 0 };
		volatile Memory m_RdBuffer = new Memory(2048);
		volatile int[] m_RdErr = { 0 };
		volatile int m_RdN[] = { 0 };
		volatile OVERLAPPED m_RdOVL = new OVERLAPPED();
		volatile Memory m_WrBuffer = new Memory(2048);
		volatile COMSTAT m_WrStat = new COMSTAT();
		volatile int[] m_WrErr = { 0 };
		volatile int m_WrN[] = { 0 };
		volatile int m_WritePending;
		volatile OVERLAPPED m_WrOVL = new OVERLAPPED();
		volatile int m_SelN[] = { 0 };
		volatile HANDLE m_CancelWaitSema4;
		volatile OVERLAPPED m_SelOVL = new OVERLAPPED();
		volatile IntByReference m_EventFlags = new IntByReference();
		volatile Termios m_Termios = new Termios();
		volatile int MSR; // initial value
		// these cached values are used to detect changes in termios structure and speed up things by avoiding unnecessary updates
		volatile int m_VTIME = -1;
		volatile int m_VMIN = -1;
		volatile int m_c_speed = -1;
		volatile int m_c_cflag = -1;
		volatile int m_c_iflag = -1;
		volatile int m_c_oflag = -1;

		synchronized public void fail() throws Fail {
			int err = GetLastError();
			Memory buffer = new Memory(2048);
			int res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, (int) buffer.size(), null);

			log = log && log(1, "fail() %s, Windows GetLastError()= %d, %s\n", lineno(1), err, buffer.getString(0, true));

			// FIXME here convert from Windows error code to 'posix' error code

			Fail f = new Fail();
			throw f;
		}

		synchronized public void lock() throws InterruptedException {
			if (m_Locked)
				wait();
			m_Locked = true;
		}

		synchronized public void unlock() {
			if (!m_Locked)
				throw new IllegalArgumentException("Port was not locked");
			m_Locked = false;
			notifyAll();
		}

		synchronized public void waitUnlock() {
			while (m_Locked) {
				try {
					wait();
				} catch (InterruptedException e) {
					// interruption cannot cancel wait
				}
			}
		}

		public Port() {
			synchronized (JTermiosImpl.this) {
				m_FD = -1;
				for (int i = 0; i < m_PortFDs.length; ++i) {
					if (!m_PortFDs[i]) {
						m_FD = i;
						m_PortFDs[i] = true;
						m_OpenPorts.put(m_FD, this);
						m_CancelWaitSema4 = CreateEventA(null, false, false, null);
						if (m_CancelWaitSema4 == null)
							throw new RuntimeException("Unexpected failure of CreateEvent() call");
						return;
					}
				}
				throw new RuntimeException("Too many ports open");
			}
		}

		public void close() {
			synchronized (JTermiosImpl.this) {
				if (m_FD >= 0) {
					m_OpenPorts.remove(m_FD);
					m_PortFDs[m_FD] = false;
					m_FD = -1;
				}

				if (m_CancelWaitSema4 != null)
					SetEvent(m_CancelWaitSema4);
				if (m_Comm != null) {
					ResetEvent(m_SelOVL.hEvent);

					if (!CancelIo(m_Comm))
						log = log && log(1, "CancelIo() failed, GetLastError()= %d, %s\n", GetLastError(), lineno(1));
					if (!PurgeComm(m_Comm, PURGE_TXABORT + PURGE_TXCLEAR + PURGE_RXABORT + PURGE_RXCLEAR))
						log = log && log(1, "PurgeComm() failed, GetLastError()= %d, %s\n", GetLastError(), lineno(1));

					GetOverlappedResult(m_Comm, m_RdOVL, m_RdN, true);
					GetOverlappedResult(m_Comm, m_WrOVL, m_WrN, true);
					GetOverlappedResult(m_Comm, m_SelOVL, m_SelN, true);
				}

				HANDLE h; // / 'hEvent' might never have been 'read' so read it
				// to this var first

				synchronized (m_RdBuffer) {
					h = (HANDLE) m_RdOVL.readField("hEvent");
					m_RdOVL = null;
					if (h != null && !h.equals(NULL) && !h.equals(INVALID_HANDLE_VALUE))
						CloseHandle(h);
				}

				synchronized (m_WrBuffer) {
					h = (HANDLE) m_WrOVL.readField("hEvent");
					m_WrOVL = null;

					if (h != null && !h.equals(NULL) && !h.equals(INVALID_HANDLE_VALUE))
						CloseHandle(h);
				}

				// Ensure that select() is through before releasing the m_SelOVL
{'fraction_non_alphanumeric': 0.10020258687860371, 'fraction_numerical': 0.007168458781362007, 'mean_word_length': 3.6980234260614933, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 12, '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': '7574470', 'n_tokens_mistral': 4897, 'n_tokens_neox': 4088, 'n_words': 1358}