Skip to content

Investigate Win32s support #4

Closed

Description

@seritools

Activity

  1. malxau commented on Apr 21, 2022

    @malxau

    Note that Win32s has no console subsystem, no support for threads, and executes in a shared address space (requiring a relocatable executable.)

  2. mateli commented on Mar 7, 2025

    @mateli

    There are hacks to mimic win32 threads in win32s but that is ugly. To begin with Win 3.1 is a single core operating system that use co-operative multitasking which is in itself ugly.

    This is a problem shared when running rust on bare metal embedded systems where there also is nothing external to provide threading and usually no hardware support for parallel execution. There is a difference between concurrency and actual parallel execution where the latter requires hardware that run more than one task at a time such as having hardware threads or multiple cores. The hardware that Win 3.1 can run on and many embedded systems have no hardware threads and a single core so no parallelism unless you actually add multiple processors to a system. There were 386 systems with multiple processors but they all ran some variant of Unix or other workstation/server operating system. Windows 3.1 or 9x never had any support for something like that but Windows NT could run on dual Pentium system which came in some workstations.

    So technically Windows 9x have fake threads with slow preemptive multitasking. Windows NT (including up to Windows 11) has support for multiple processors and multiple cores meaning real threads.

    To achieve concurrency it is best to either rely on non-threading concurrency model or some kind of virtual threads inside rust. Even on modern system it is unwise to start more OS threads than there are cores or hardware threads capabilities. On PC hardware threads means HyperThreading where one actual core show up as two different cores.

    So if you have 4 real cores with hyperthreading you should never have more than 8 active threads in the entire operating system because that will have a significant negative impact on OS performance. And in many scenarios running 8 threads instead of four will have a serious impact. That is why many scientific applications like matlab is best run with hyperthreading inactive and one thread per physical core.

    A bit of a rant but the point is that on Windows 3.1 and windows 9x you should not have more than one thread. Use other concurrency like async await that plays well on a single core processor.

    The solution to is to make starting a new thread fail. Simple as that. There is no reason to have threads on Windows 3.1, or Windows 9x.

  3. mateli commented on Mar 7, 2025

    @mateli

    As for a console subsystem... just make a window and put text in it... we don't need a full ANSI terminal window. Sure if we are going to run a series of console application we may need a simple command interpreter but that is not difficult to implement.

  4. ssokolow commented on Mar 7, 2025

    @ssokolow

    As someone who lived and programmed through the Windows 9x to single-core Windows XP era, I'm calling [citation needed] on your blanket statement to steer away from threading with only one CPU core.

    Yes, it's inefficient to have more than cores+1 CPU-bound threads (hence the classic advice to feed -j 3 to GNU Make if you've got 2 CPU cores so you've got two CPU-bound threads and one to take up the slack when the other two are switching to the next compilation unit), but arguing to just blanket avoid parallelism when you've got one CPU core is like arguing to avoid 3D rendering unless you've got VR/AR goggles.

    A bit of a rant but the point is that on Windows 3.1 and windows 9x you should not have more than one thread. Use other concurrency like async await that plays well on a single core processor.

    As someone who is now a retro-hobbyist, I'll say that one of the most noticeable ways Microsoft leap-frogged Apple with Windows 9x is that, even on Mac OS 9.2.2 on a 933MHz Power Mac G4 Quicksilver 2002, you can feel the UI struggling to remain responsive while something like the official Mac OS Finder file-copy dialog is running, thanks to the lack of preemption. By contrast, the UI remains snappy under Windows 98SE on my 133MHz Pentium 1.

    Is implementing preemption for Windows 3.1 overkill? Sure... but threading was one of the big things that made Windows 9x great.

    EDIT: TL;DR: Threading isn't just about CPU-bound parallelism. It's also akin to the "VLIW vs. runtime branch prediction" conundrum, where it turns out to be more difficult in practice than you expect to reliably and effectively pick yield points in I/O-bound tasks to ensure the the UI remains responsive.

  5. ssokolow commented on Mar 7, 2025

    @ssokolow

    As for a console subsystem... just make a window and put text in it... we don't need a full ANSI terminal window. Sure if we are going to run a series of console application we may need a simple command interpreter but that is not difficult to implement.

    *nod* That's how Open Watcom C/C++ does it if you ask it to build a Win16 app with console APIs.

  6. mateli commented on Mar 7, 2025

    @mateli

    I am talking out of my own experience.

    What I am saying is that running more than one ACTIVE thread/process at a time is less efficient than single-thread concurrency. GCC does not have single-thread concurrency but instead often becomes inactive waiting for IO operations etc. Unless we have a compiler that use async/await or any other thread-less concurrency to compare with we cannot use compilers to test my statement. There are compilers that are significantly faster than GCC such as Turbo Pascal however as that is proprietary code it is difficult to say why it is faster, but I doubt it is by spawning threads.

    I have written quite extensive single-tread GUI:s in C# using async/await and they do not struggle to be responsive. The cooperative multitasking is handled every time an async function calls another async function because that always pushes control back to dotnet's task scheduler.

    It is true that both windows 3.1 and MacOS had problems with stability due to tasks that becomes inactive without handing over control to the scheduler. Those are bugs and while they still can occur with async-await frameworks and similar they are a lot less frequent. It is similarly possible and frequently occurring that modern multi-threaded system grinds to a halt if software has bugs that the OS cannot handle. Which is why patterns that minimizes bugs are important no matter how fancy your OS is.

    Note that on these old operative system cooperative multitasking were implemented by having tasks returning control by making a specific call into the kernel. With modern cooperative multitasking (my experience is mostly with C# so I will reference that although I know that rust has similar mechanisms) any call from an async function to another async function returns control back to the scheduler. Whenever a non-async function are used and an async is available this will produce a compiler warning.

    That being said with cooperative multitasking you still of course need to know what you are doing and appropriately test your code.

    And finally my point is that with non-NT Windows it is better to use async/await patterns than to use threading. Same for modern systems unless you are actually spreading calculations across cores then you need threads, but this is something non-NT windows has no support for.

    Also not that there are a lot of embedded software that has a design similar to Windows 3.1 and old MacOS without having the problems that you pointed out with cooperative multitasking. The reason that they do not have these problems are testing, testing and more testing. With the testing frameworks the only cause for such a system to become unresponsive is if we take shortcuts with writing good tests, and of course running those tests regularly.

    We should remember that the cooperative desktop systems from the 1980:s and their software were written over a short period of time and practices like extensive testing were simply just not seen as important. This is why even the software that were part of these operating systems had plenty of bugs that had performance and stability implications that simply would not be there in for example a robot control system of similar architecture and complexity.

    What I am not claiming and isn't going to claim is that writing cooperative well-performing concurrent code is as easy as just spawning threads. But what I claim is that when you have such code and it contains no bugs that causes major inactivity, it performs a lot better than spawning threads. With the exception of when you actually need to offload to another hardware execution unit.

    Finally as a tool to write Windows 3.1 applications, using rust with async-await is going to be the easiest way to write a stable Windows 3.1 application. Just make the internal scheduler regularly hand over control to the OS.

  7. ssokolow commented on Mar 7, 2025

    @ssokolow

    What I am not claiming and isn't going to claim is that writing cooperative well-performing concurrent code is as easy as just spawning threads. But what I claim is that when you have such code and it contains no bugs that causes major inactivity, it performs a lot better than spawning threads. With the exception of when you actually need to offload to another hardware execution unit.

    That's basically my point. Telling people to just write correct cooperative multithreading on Windows 9x has a tone-deafness to it akin to saying that you only need Rust if you're working in a team and solo programmers should just write correct C++.

  8. mateli commented on Mar 7, 2025

    @mateli

    Well there is plenty of evidence that it is fully possible to do so. The difference between C++ and other languages are not between the possibility of writing correct code but about how much it costs. Writing correct cooperative multitasking using a modern framework like the one in C# and probably the one in Rust is going to cost a lot less than doing it using pretty much any compiler that were available for the discussed platforms in the era that they were popular.

    On the other hand a lot of the cooperative multitasking that are in embedded systems is written in C and and works very well. The main reason for this is simply that embedded developer spend a lot of time making sure that their code is correct and this of course makes such projects expensive (when done right). Automated testing and frameworks helps keeping the cost down compared to something written before those tools were available but you still need to have a significant budget for testing. Although that being said a lot of the Windows 3.1 software were remarkable stable given the fact the only method for making it so were having a developer that wrote correct code.

    So no I do not agree to this. There are to much code written in languages like C that use cooperative multitasking and works perfectly well without freezing and crashing. And no matter what compilers and tools you use in order to improve the development process no system are going to be fit for production use without appropriate testing.

  9. mateli commented on Mar 7, 2025

    @mateli

    As for a console subsystem... just make a window and put text in it... we don't need a full ANSI terminal window. Sure if we are going to run a series of console application we may need a simple command interpreter but that is not difficult to implement.

    nod That's how Open Watcom C/C++ does it if you ask it to build a Win16 app with console APIs.

    Ah I didn't know that. I actually have plans of porting a console game to Win16 and were thinking about writing something like that, but if Watcom already has it I don't need to.

  10. ssokolow commented on Mar 7, 2025

    @ssokolow

    Ah I didn't know that. I actually have plans of porting a console game to Win16 and were thinking about writing something like that, but if Watcom already has it I don't need to.

    I don't know how rich an API it provides. I've only ever poked at it as a replacement for very basic use of CMD.EXE for output.

  11. ssokolow commented on Mar 7, 2025

    @ssokolow

    Well there is plenty of evidence that it is fully possible to do so. The difference between C++ and other languages are not between the possibility of writing correct code but about how much it costs. Writing correct cooperative multitasking using a modern framework like the one in C# and probably the one in Rust is going to cost a lot less than doing it using pretty much any compiler that were available for the discussed platforms in the era that they were popular.

    You're still assuming that the users are willing to reinvent or use Rust versions of everything they need which would otherwise be blocking-API'd if they're looking at the period dependencies. There's a reason tokio has spawn_blocking.

  12. locked and limited conversation to collaborators on Mar 7, 2025
  13. converted this issue into a discussion #48 on Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions