Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #67263 > unrolled thread

Why can't I understand what coroutines are?

Started byJuha Nieminen <nospam@thanks.invalid>
First post2019-08-04 09:37 +0000
Last post2019-08-05 07:46 +0000
Articles 20 on this page of 72 — 19 participants

Back to article view | Back to comp.lang.c++


Contents

  Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-04 09:37 +0000
    Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-04 02:58 -0700
      Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 09:18 -0400
        Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-04 11:15 -0700
    Re: Why can't I understand what coroutines are? Louis Krupp <lkrupp@nospam.pssw.com.invalid> - 2019-08-04 04:58 -0600
      Re: Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-05 08:31 +0000
        Re: Why can't I understand what coroutines are? Martijn van Buul <pino@dohd.org> - 2019-08-05 09:51 +0000
          Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-05 14:44 -0700
    Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-04 14:08 +0100
      Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 09:23 -0400
        Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-04 14:36 +0100
          Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 10:31 -0400
            Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-04 16:42 +0100
              Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 16:43 -0400
                Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-04 13:58 -0700
                  Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 17:36 -0400
                    Re: Why can't I understand what coroutines are? Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk> - 2019-08-04 22:43 +0100
                    Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-04 22:52 +0100
                      Re: Why can't I understand what coroutines are? Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk> - 2019-08-04 23:10 +0100
                      Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 21:32 -0400
                        Re: Why can't I understand what coroutines are? Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk> - 2019-08-05 18:32 +0100
                          Re: Why can't I understand what coroutines are? scott@slp53.sl.home (Scott Lurndal) - 2019-08-05 17:48 +0000
                            Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-05 20:14 -0400
                            Re: Why can't I understand what coroutines are? leigh.v.johnston@googlemail.com - 2019-08-06 04:13 -0700
                              Re: Why can't I understand what coroutines are? leigh.v.johnston@googlemail.com - 2019-08-06 04:23 -0700
                                Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-06 22:19 +0100
                                  Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-06 15:19 -0700
                                    Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-07 00:01 +0100
                                      Re: Why can't I understand what coroutines are? scott@slp53.sl.home (Scott Lurndal) - 2019-08-06 23:52 +0000
                                      Re: Why can't I understand what coroutines are? Öö Tiib <ootiib@hot.ee> - 2019-08-06 16:57 -0700
                                        Re: Why can't I understand what coroutines are? scott@slp53.sl.home (Scott Lurndal) - 2019-08-07 14:41 +0000
                                          Re: Why can't I understand what coroutines are? Öö Tiib <ootiib@hot.ee> - 2019-08-08 07:10 -0700
                                    Re: Why can't I understand what coroutines are? scott@slp53.sl.home (Scott Lurndal) - 2019-08-06 23:49 +0000
                    Re: Why can't I understand what coroutines are? Robert Wessel <robertwessel2@yahoo.com> - 2019-08-04 22:04 -0500
                    Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-04 20:10 -0700
          Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-04 11:20 -0700
            Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-04 20:30 +0100
              Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-04 14:03 -0700
              Re: Why can't I understand what coroutines are? Paavo Helde <myfirstname@osa.pri.ee> - 2019-08-05 09:36 +0300
                Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-05 14:34 -0700
          Re: Why can't I understand what coroutines are? scott@slp53.sl.home (Scott Lurndal) - 2019-08-04 19:46 +0000
        Re: Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-05 08:37 +0000
          Re: Why can't I understand what coroutines are? David Brown <david.brown@hesbynett.no> - 2019-08-05 12:06 +0200
            Re: Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-05 12:20 +0000
              Re: Why can't I understand what coroutines are? David Brown <david.brown@hesbynett.no> - 2019-08-06 15:06 +0200
                Re: Why can't I understand what coroutines are? David Brown <david.brown@hesbynett.no> - 2019-08-06 15:18 +0200
            Re: Why can't I understand what coroutines are? Bonita Montero <Bonita.Montero@gmail.com> - 2019-08-06 13:58 +0200
    Re: Why can't I understand what coroutines are? Sam <sam@email-scan.com> - 2019-08-04 09:17 -0400
    Re: Why can't I understand what coroutines are? Robert Wessel <robertwessel2@yahoo.com> - 2019-08-04 10:12 -0500
      Re: Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-05 08:42 +0000
        Re: Why can't I understand what coroutines are? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-08-05 13:35 +0100
        Re: Why can't I understand what coroutines are? Bart <bc@freeuk.com> - 2019-08-05 20:19 +0100
        Re: Why can't I understand what coroutines are? Christian Gollwitzer <auriocus@gmx.de> - 2019-08-05 22:26 +0200
          Re: Why can't I understand what coroutines are? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-05 23:09 +0200
            Re: Why can't I understand what coroutines are? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-09 19:49 -0700
              Re: Why can't I understand what coroutines are? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-10 05:13 +0200
            Re: Why can't I understand what coroutines are? Christian Gollwitzer <auriocus@gmx.de> - 2019-08-10 08:23 +0200
              Re: Why can't I understand what coroutines are? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-10 10:43 +0200
                Re: Why can't I understand what coroutines are? Christian Gollwitzer <auriocus@gmx.de> - 2019-08-11 22:19 +0200
                  Re: Why can't I understand what coroutines are? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-14 08:25 -0700
          Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-10 00:19 -0700
          Re: Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-11 21:16 +0000
        Re: Why can't I understand what coroutines are? "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-08-05 23:43 -0700
        Re: Why can't I understand what coroutines are? Bonita Montero <Bonita.Montero@gmail.com> - 2019-08-10 10:50 +0200
    Re: Why can't I understand what coroutines are? Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk> - 2019-08-04 17:57 +0100
    Re: Why can't I understand what coroutines are? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-05 01:11 +0200
      Re: Why can't I understand what coroutines are? Juha Nieminen <nospam@thanks.invalid> - 2019-08-05 08:50 +0000
        Re: Why can't I understand what coroutines are? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-05 16:59 +0200
      Re: Why can't I understand what coroutines are? Manfred <noname@add.invalid> - 2019-08-06 16:24 +0200
      Re: Why can't I understand what coroutines are? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-07 00:52 +0200
        Re: Why can't I understand what coroutines are? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-09 18:11 -0700
    Re: Why can't I understand what coroutines are? Martijn van Buul <pino@dohd.org> - 2019-08-05 07:46 +0000

Page 1 of 4  [1] 2 3 4  Next page →


#67263 — Why can't I understand what coroutines are?

FromJuha Nieminen <nospam@thanks.invalid>
Date2019-08-04 09:37 +0000
SubjectWhy can't I understand what coroutines are?
Message-ID<qi6910$1eht$1@adenine.netfront.net>
C++20 will introduce support for coroutines. To this day, for reasons
that are completely mysterious even to me, I have *absolutely no idea*
what they are. I just don't get it.

I go to the Wikipedia page "Coroutine"... and it tells me nothing.

"Coroutines are computer program components that generalize subroutines
for non-preemptive multitasking, by allowing execution to be suspended
and resumed. Coroutines are well-suited for implementing familiar program
components such as cooperative tasks, exceptions, event loops, iterators,
infinite lists and pipes."

This tells me nothing. I don't get it.

"By contrast, coroutines can exit by calling other coroutines, which may
later return to the point where they were invoked in the original
coroutine; from the coroutine's point of view, it is not exiting but
calling another coroutine."

Can exit by calling other coroutines... And then the execution later
resumes from that calling point. This is not "exiting" but "yielding",
whatever that's supposed to mean.

Sounds like a normal function call to me, but what do I know? I can't
understand what this is saying. It's like it's speaking in legible
English, yet it somehow is complete gibberish at the same time. I don't
get it.

It then gives a pseudocode example of coroutines... that look exactly
like a function calling another (which then just returns to the
original caller).

==========================================
var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce
==========================================

This looks to me exactly like

==========================================
var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        call consume

coroutine consume
    while q is not empty
        remove some items from q
        use the items
    return
==========================================

But what do I know? I don't get it.

Ok, Wikipedia is just a generic article about coroutines. Maybe
https://en.cppreference.com/w/cpp/language/coroutines has a better
explanation. It's done from the perspective of C++ in particular,
after all.

"A coroutine is a function that can suspend execution to be resumed
later. Coroutines are stackless: they suspend execution by returning
to the caller. This allows for sequential code that executes
asynchronously (e.g. to handle non-blocking I/O without explicit
callbacks), and also supports algorithms on lazy-computed infinite
sequences and other uses."

A function that can suspend execution to be resumed later...
Yeah, I don't get it. Allows sequential code that executes
asynchronously... What? Supports algorithms on lazy-computed infinite
sequences... Ok, but what does this all mean? I don't get it.

The examples given are complete gibberish. I have *absolutely no
idea* what something like this is supposed to mean:

    co_await async_write(socket, buffer(data, n));

co_await? What?

It seems that everybody else is awaiting (hah!) and praising coroutines
like the second coming of Christ. Yet I just can't understand what they
are, or how exactly they are used or useful. All web pages I can find
are legible English that still somehow manages to be completely meaningless
gibberish that tells me nothing.

[toc] | [next] | [standalone]


#67264

From"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>
Date2019-08-04 02:58 -0700
Message-ID<qi6a8a$1086$1@gioia.aioe.org>
In reply to#67263
On 8/4/2019 2:37 AM, Juha Nieminen wrote:
> C++20 will introduce support for coroutines. To this day, for reasons
> that are completely mysterious even to me, I have *absolutely no idea*
> what they are. I just don't get it.
[...]

They are like Fibers on Windows:

https://docs.microsoft.com/en-us/windows/win32/procthread/fibers

http://man7.org/linux/man-pages/man3/getcontext.3.html

https://docs.microsoft.com/en-us/windows/win32/procthread/user-mode-scheduling

[toc] | [prev] | [next] | [standalone]


#67269

FromSam <sam@email-scan.com>
Date2019-08-04 09:18 -0400
Message-ID<cone.1564924708.153661.25830.1004@monster.email-scan.com>
In reply to#67264

[Multipart message — attachments visible in raw view] — view raw

Chris M. Thomasson writes:

> On 8/4/2019 2:37 AM, Juha Nieminen wrote:
>> C++20 will introduce support for coroutines. To this day, for reasons
>> that are completely mysterious even to me, I have *absolutely no idea*
>> what they are. I just don't get it.
> [...]
>
> They are like Fibers on Windows:

That's because Windows always sucked at multithreading.


[toc] | [prev] | [next] | [standalone]


#67277

From"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>
Date2019-08-04 11:15 -0700
Message-ID<qi77ct$1174$2@gioia.aioe.org>
In reply to#67269
On 8/4/2019 6:18 AM, Sam wrote:
> Chris M. Thomasson writes:
> 
>> On 8/4/2019 2:37 AM, Juha Nieminen wrote:
>>> C++20 will introduce support for coroutines. To this day, for reasons
>>> that are completely mysterious even to me, I have *absolutely no idea*
>>> what they are. I just don't get it.
>> [...]
>>
>> They are like Fibers on Windows:
> 
> That's because Windows always sucked at multithreading.
> 
> 

lol. ;^)

[toc] | [prev] | [next] | [standalone]


#67265

FromLouis Krupp <lkrupp@nospam.pssw.com.invalid>
Date2019-08-04 04:58 -0600
Message-ID<bpcdkelmgr0fltsvlm9qdiq5ufsqgii0qa@4ax.com>
In reply to#67263
On Sun, 4 Aug 2019 09:37:39 -0000 (UTC), Juha Nieminen
<nospam@thanks.invalid> wrote:

>C++20 will introduce support for coroutines. To this day, for reasons
>that are completely mysterious even to me, I have *absolutely no idea*
>what they are. I just don't get it.
<snip>

It might help to look at coroutines in another language, say Lua.

A main program and a coroutine cooperate like this:

The main program creates the coroutine object, associating it with a
function to be executed. At some point, the main program hands control
to the coroutine, possibly passing it arguments, and then waits for
the coroutine to either yield (or return) and possibly pass back
results. When the main program gets around to it, it resumes the
coroutine and waits again for it to yield or return.

The coroutine wakes up when it's told, possibly accepting arguments,
and then does whatever it wants until it decides it's time to either
yield control back to the main program or return, possibly passing
results back to the main program. A coroutine that yields waits until
it's told to resume by the main program; a coroutine that returns
simply goes away and cannot be told to resume.

Here's a simple example of a program that does a couple of trivial
computations and then quits:

===

-- Function to run in coroutine
--
function cf(n)
    print("coroutine beginning n = ", n)
    k = n * n
    print("coroutine yielding, returning ", k)
    coroutine.yield(k)

    print("coroutine resuming")
    k = k * n
    print("coroutine yielding, returning", k)
    coroutine.yield(k)

    print("coroutine resuming")
    k = -1
    print("coroutine all done, returning", k)
    return(k)
end

-- Main program
--
print("main creating coroutine")
co = coroutine.create(cf)

print("main telling coroutine to resume (actually, to start)")
print("main waiting for coroutine to yield or return")
status, result = coroutine.resume(co, 7)
print("main result from coroutine = ", result)

print("main telling coroutine to resume")
print("main waiting for coroutine to yield or return")
status, result = coroutine.resume(co)
print("main result from coroutine = ", result)

print("main telling coroutine to resume")
print("main waiting for coroutine to yield or return")
status, result = coroutine.resume(co)
print("main result from coroutine = ", result)

===

The output:

main creating coroutine
main telling coroutine to resume (actually, to start)
main waiting for coroutine to yield or return
coroutine beginning n = 	7
coroutine yielding, returning 	49
main result from coroutine = 	49
main telling coroutine to resume
main waiting for coroutine to yield or return
coroutine resuming
coroutine yielding, returning	343
main result from coroutine = 	343
main telling coroutine to resume
main waiting for coroutine to yield or return
coroutine resuming
coroutine all done, returning	-1
main result from coroutine = 	-1

I hope this helps a little.

Louis

[toc] | [prev] | [next] | [standalone]


#67298

FromJuha Nieminen <nospam@thanks.invalid>
Date2019-08-05 08:31 +0000
Message-ID<qi8pgi$4c5$1@adenine.netfront.net>
In reply to#67265
Louis Krupp <lkrupp@nospam.pssw.com.invalid> wrote:
> A main program and a coroutine cooperate like this:
> 
> The main program creates the coroutine object, associating it with a
> function to be executed. At some point, the main program hands control
> to the coroutine, possibly passing it arguments, and then waits for
> the coroutine to either yield (or return) and possibly pass back
> results. When the main program gets around to it, it resumes the
> coroutine and waits again for it to yield or return.
> 
> The coroutine wakes up when it's told, possibly accepting arguments,
> and then does whatever it wants until it decides it's time to either
> yield control back to the main program or return, possibly passing
> results back to the main program. A coroutine that yields waits until
> it's told to resume by the main program; a coroutine that returns
> simply goes away and cannot be told to resume.

I'm still not sure how that's different from a regular function.

Or, perhaps more precisely, a lambda function (because as far as
I understand, a coroutine can have its own state, just like a
lambda can have captured variables, making the lambda effectively
a stateful functor.)

Is the difference that a coroutine can "return" (so to speak)
from anywhere inside itself, and the next time it gets "called"
again it resumes from that point forward, rather than from the
beginning (like a lambda would)?

I'm still not exactly sure how that's so useful in practice
(compared to stateful functors, like lambdas.)

[toc] | [prev] | [next] | [standalone]


#67302

FromMartijn van Buul <pino@dohd.org>
Date2019-08-05 09:51 +0000
Message-ID<5d47fc05$0$2234$e4fe514c@news.kpn.nl>
In reply to#67298
* Juha Nieminen:

> Is the difference that a coroutine can "return" (so to speak)
> from anywhere inside itself, and the next time it gets "called"
> again it resumes from that point forward, rather than from the
> beginning (like a lambda would)?

Yup. It acts like a blocking call, in a way.

> I'm still not exactly sure how that's so useful in practice
> (compared to stateful functors, like lambdas.)

Consider a producer/consumer implemented using threads. In this
case 'publish' and 'consume' operate on some kind of queue, possibly
with a depth of 1. If a producer publishes something to a full queue,
it blocks. If the consumer tries to consume from an empty queue, it
blocks.

class ProducerClass 
{
  [...]

  void mainLoop()
  {
      while ( ... )
      {
         Item newItem;

	 [... do work on newItem ...]

	 publish( newItem);
      }
  }
}

class ConsumerClass 
{
  [...]

  void mainLoop()
  {
      while ( ... )
      {
	 std::vector<Item> packet;
	 packet.reserve(10);

	 for (int i = 0; i < packet.capacity(); ++i)
	 {
	    Item newItem = consume();

	    [.. possibly do something on newItem ...]
	    packet.push_back(newItem);
	 }

	 [... do something on a packet of 10 items ...]

      }
  }
}

In this case, having the consumer and producer execute in their own
thread offers no performance benefit, but it might still be very beneficial
to implement it this way, depending on details of either consumer or
producer. As with all examples, the above example is a bit silly.

In this case, with a 1:1 relationship between consumer and producer, such
a threaded solution would offer no parallelisation, so using threads here
only serves to simplify the implementation of the consumer or producer -
performancewise it's detrimental. In this case, coroutines would offer
a compromise: The separation and implementation benefits of a threaded
solution, without the overhead caused by blocking.

Another possible use of coroutines would be iterators. Suppose I have a

struct Foo
{

};

struct Bar
{
   [...]
   std::list<Foo> foos;
}

struct Quux
{
   [...]
   std::vector<Bar> bars;
}

struct Wibble
{
   std::map< ..., Quux> quuxMap;
}


Suppose I want to offer a way to iterate over all instances of "Foo" inside
a "Wibble", without exposing any of the classes inbetween (because they're 
private to an implementation, for example). There are numerous options:

* Create a std::container<Foo> GetFoos(const Wibble &) method. Could be
  expensive (because Foo is expensive to copy), impossible (because
  Foo has a deleted copy constructor) - and is generally undesireable 
  anyway.
* Create an 

  ApplyOnFoos(const Wibble &, const std::function<void(const Foo&)> &callback)

  method (or something similar using templates, details schmetails) that 
  iterates over all elements and calls the callback function for every
  instance of 'Foo'. Could work, but the receiving end might have
  to capture quite a bit to make this work.
* Create an iterator class along the line of

  class outputFunctor
  {
    outputFunctor(Wibble &) {...}

    std::optional<std::reference_wrapper<Foo>> operator() {...}
  }

  (Or create an STL iterator, which is essentially the same). Will 
  definately work, but implementation is going to be awkward.
  
The first two have the benefit that their implementation can use simple
constructs (more to the point: A nested range-based for loop will do the
trick). Without coroutines, implementing the functor will require storing
iterators to the intermediate class inside the functor. With coroutines,
you can implement it using the same simple nested-range for loop.

I haven't touched C++'s coroutines yet (I'm waiting for C++20 to arrive)
so I don't know whether the required boilerplate will offset any 
implementation benefits, but in Lua it would be something like

function outputIterator(wibble)
  local _generator = coroutine.create( 
    function()
      for (_, quux) in pairs(wibble.quuxMap) do
	for (_, bar) in pairs(quux.bars) do
          for(_, foo) in pairs(bar.foos) do
            coroutine.yield(foo)
	  end
	end
      end
    end)

  return function ()
    local _, nextFoo = coroutine.resume(_generator)
    return nextFoo
  end
end

usage:

 local myWbble = [....]

 for (foo in outputIterator(myWibble)) do
    print ("Yay, a foo!", tostring(foo))
 end

(Untested, and my lua is a bit rusty)

-- 
Martijn van Buul - pino@dohd.org 

[toc] | [prev] | [next] | [standalone]


#67322

From"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>
Date2019-08-05 14:44 -0700
Message-ID<qia80k$a7q$1@gioia.aioe.org>
In reply to#67302
On 8/5/2019 2:51 AM, Martijn van Buul wrote:
> * Juha Nieminen:
> 
>> Is the difference that a coroutine can "return" (so to speak)
>> from anywhere inside itself, and the next time it gets "called"
>> again it resumes from that point forward, rather than from the
>> beginning (like a lambda would)?
> 
> Yup. It acts like a blocking call, in a way.
> 
>> I'm still not exactly sure how that's so useful in practice
>> (compared to stateful functors, like lambdas.)
> 
> Consider a producer/consumer implemented using threads. In this
> case 'publish' and 'consume' operate on some kind of queue, possibly
> with a depth of 1. If a producer publishes something to a full queue,
> it blocks. If the consumer tries to consume from an empty queue, it
> blocks.
> 
> class ProducerClass
> {
>    [...]
> 
>    void mainLoop()
>    {
>        while ( ... )
>        {
>           Item newItem;
> 
> 	 [... do work on newItem ...]
> 
> 	 publish( newItem);
>        }
>    }
> }
> 
> class ConsumerClass
> {
>    [...]
> 
>    void mainLoop()
>    {
>        while ( ... )
>        {
> 	 std::vector<Item> packet;
> 	 packet.reserve(10);
> 
> 	 for (int i = 0; i < packet.capacity(); ++i)
> 	 {
> 	    Item newItem = consume();
> 
> 	    [.. possibly do something on newItem ...]
> 	    packet.push_back(newItem);
> 	 }
> 
> 	 [... do something on a packet of 10 items ...]
> 
>        }
>    }
> }
> 
> In this case, having the consumer and producer execute in their own
> thread offers no performance benefit, but it might still be very beneficial
> to implement it this way, depending on details of either consumer or
> producer. As with all examples, the above example is a bit silly.
> 
> In this case, with a 1:1 relationship between consumer and producer, such
> a threaded solution would offer no parallelisation, so using threads here
> only serves to simplify the implementation of the consumer or producer -
> performancewise it's detrimental. [...]

Actually, single producer, single consumer queues are pretty nice. The 
consumer thread can work on things without bothering the procuder 
thread, and vise versa. The sync can be implemented without using any 
atomic RMW.

[toc] | [prev] | [next] | [standalone]


#67267

FromChris Vine <chris@cvine--nospam--.freeserve.co.uk>
Date2019-08-04 14:08 +0100
Message-ID<20190804140857.f1a22df42a76938b234edf4b@cvine--nospam--.freeserve.co.uk>
In reply to#67263
On Sun, 4 Aug 2019 09:37:39 -0000 (UTC)
Juha Nieminen <nospam@thanks.invalid> wrote:
[snip]
> A function that can suspend execution to be resumed later...
> Yeah, I don't get it. Allows sequential code that executes
> asynchronously... What? Supports algorithms on lazy-computed infinite
> sequences... Ok, but what does this all mean? I don't get it.
> 
> The examples given are complete gibberish. I have *absolutely no
> idea* what something like this is supposed to mean:
> 
>     co_await async_write(socket, buffer(data, n));
> 
> co_await? What?
> 
> It seems that everybody else is awaiting (hah!) and praising coroutines
> like the second coming of Christ. Yet I just can't understand what they
> are, or how exactly they are used or useful. All web pages I can find
> are legible English that still somehow manages to be completely meaningless
> gibberish that tells me nothing.

You have summarized coroutines quite well in your posting, and I
shouldn't get too hung up on one particular implementation (the one
which may be included in C++20).

The easiest coroutines to understand, and the ones used to implement
"await" semantics on asynchronous operations, are asymmetrical
coroutines implemented using delimited continuations.  These normally
have only two control-flow operations, namely a "yield" or "suspend"
operation, and a "resume" operation.  When a function executes a
"yield" or "suspend" operation, it returns control to its caller,
somewhat like a return statement.  However, unlike a return statement,
a "resume" operation on the coroutine in question will return program
execution to the point at which the yield previously occurred.

Asynchronous programming with callbacks normally works so that when a
i/o operation might block because the resource in question (read or
write) is unavailable, instead of blocking the function returns
immediately and registers a callback with the program's event loop
(usually based on select() or poll()), representing the i/o operation's
continuation when the resource becomes available.  Programming this way
using callbacks in the style of, say, Node.js is troublesome because it
gives rise to what is sometimes called inversion of control, aka
"callback hell".  In particular, control flow ceases to appear as a
sequential series of i/o operations which can easily be reasoned about.

Coroutines can offer a way out because when an i/o resource is
unavailable the i/o operation concerned can (i) register a callback
with the event loop which resumes the operation when the resource
becomes available, and then (ii) suspends to the caller (which
indirectly is the event loop).  When the resource becomes available the
event loop resumes the coroutine at the point of suspension, so
"tricking" the control flow so that it appears to the coder to be
sequential (like blocking i/o) even though it is in fact executing
asynchronously via an event loop.

The scheme language has first-class continuations built into the
language which makes implementing coroutines relatively trivial.  Here
is one library which uses asymmetrical coroutines for the purposes
mentioned above which may or may not help:
https://github.com/ChrisVine/chez-a-sync/wiki .
ECMAscript and python also now have somewhat more limited asymmetrical
coroutines.

[toc] | [prev] | [next] | [standalone]


#67270

FromSam <sam@email-scan.com>
Date2019-08-04 09:23 -0400
Message-ID<cone.1564925032.455203.25830.1004@monster.email-scan.com>
In reply to#67267

[Multipart message — attachments visible in raw view] — view raw

Chris Vine writes:

> Asynchronous programming with callbacks normally works so that when a
> i/o operation might block because the resource in question (read or
> write) is unavailable, instead of blocking the function returns
> immediately and registers a callback with the program's event loop
> (usually based on select() or poll()), representing the i/o operation's
> continuation when the resource becomes available.  Programming this way
> using callbacks in the style of, say, Node.js is troublesome because it
> gives rise to what is sometimes called inversion of control, aka
> "callback hell".  In particular, control flow ceases to appear as a
> sequential series of i/o operations which can easily be reasoned about.

This problem was solved a long time ago. It is called "threads".

The funniest thing about this is that Gnome is most extreme case of this.  
The entire Gnome stack is just one event dispatching main loop. Everything  
is callbacks. But since Gnome is C code, none of this will help it, that's  
the funny part.

[toc] | [prev] | [next] | [standalone]


#67271

FromChris Vine <chris@cvine--nospam--.freeserve.co.uk>
Date2019-08-04 14:36 +0100
Message-ID<20190804143647.511134ccbc3ef6771a692cf7@cvine--nospam--.freeserve.co.uk>
In reply to#67270
On Sun, 04 Aug 2019 09:23:52 -0400
Sam <sam@email-scan.com> wrote:
> Chris Vine writes:
> 
> > Asynchronous programming with callbacks normally works so that when a
> > i/o operation might block because the resource in question (read or
> > write) is unavailable, instead of blocking the function returns
> > immediately and registers a callback with the program's event loop
> > (usually based on select() or poll()), representing the i/o operation's
> > continuation when the resource becomes available.  Programming this way
> > using callbacks in the style of, say, Node.js is troublesome because it
> > gives rise to what is sometimes called inversion of control, aka
> > "callback hell".  In particular, control flow ceases to appear as a
> > sequential series of i/o operations which can easily be reasoned about.
> 
> This problem was solved a long time ago. It is called "threads".
> 
> The funniest thing about this is that Gnome is most extreme case of this.  
> The entire Gnome stack is just one event dispatching main loop. Everything  
> is callbacks. But since Gnome is C code, none of this will help it, that's  
> the funny part.

Threads are very definitely not the answer.  Threads work well with
cpu-bound workloads.  They are hopeless with i/o-bound workloads, which
is why co-operative (possibly single-threaded) multi-tasking using
coroutines has become popular.

If you use native OS threads for i/o bound workloads, you are doing it
wrong.

[toc] | [prev] | [next] | [standalone]


#67272

FromSam <sam@email-scan.com>
Date2019-08-04 10:31 -0400
Message-ID<cone.1564929062.699026.27147.1004@monster.email-scan.com>
In reply to#67271

[Multipart message — attachments visible in raw view] — view raw

Chris Vine writes:

> On Sun, 04 Aug 2019 09:23:52 -0400
> Sam <sam@email-scan.com> wrote:
> >
> > The funniest thing about this is that Gnome is most extreme case of this.
> > The entire Gnome stack is just one event dispatching main loop. Everything
> > is callbacks. But since Gnome is C code, none of this will help it, that's
> > the funny part.
>
> Threads are very definitely not the answer.  Threads work well with
> cpu-bound workloads.  They are hopeless with i/o-bound workloads, which
> is why co-operative (possibly single-threaded) multi-tasking using
> coroutines has become popular.

So instead of simply spawning off a separate thread whose only job is a  
laughably simple loop that wait for I/O to happen, and that do something  
simple with it, like saving some data that's read from the socket into a  
file, it's much easier to build another rube-goldbergian event-loop based  
contraption, and the entire application now must be dragged, kicking and  
screaming, into rebuilding under that framework? That's the easier approach?

Coroutines are not going to be better at this, either.

> If you use native OS threads for i/o bound workloads, you are doing it
> wrong.

Nope. One only needs to understand that I/O bound workloads is not the only  
reason that threads exist.

[toc] | [prev] | [next] | [standalone]


#67274

FromChris Vine <chris@cvine--nospam--.freeserve.co.uk>
Date2019-08-04 16:42 +0100
Message-ID<20190804164254.8072d620ed92d63e3d8c9d42@cvine--nospam--.freeserve.co.uk>
In reply to#67272
On Sun, 04 Aug 2019 10:31:02 -0400
Sam <sam@email-scan.com> wrote:
> Chris Vine writes:
> 
> > On Sun, 04 Aug 2019 09:23:52 -0400
> > Sam <sam@email-scan.com> wrote:
> > >
> > > The funniest thing about this is that Gnome is most extreme case of this.
> > > The entire Gnome stack is just one event dispatching main loop. Everything
> > > is callbacks. But since Gnome is C code, none of this will help it, that's
> > > the funny part.
> >
> > Threads are very definitely not the answer.  Threads work well with
> > cpu-bound workloads.  They are hopeless with i/o-bound workloads, which
> > is why co-operative (possibly single-threaded) multi-tasking using
> > coroutines has become popular.
> 
> So instead of simply spawning off a separate thread whose only job is a  
> laughably simple loop that wait for I/O to happen, and that do something  
> simple with it, like saving some data that's read from the socket into a  
> file, it's much easier to build another rube-goldbergian event-loop based  
> contraption, and the entire application now must be dragged, kicking and  
> screaming, into rebuilding under that framework? That's the easier approach?

You have it wrong.

For toy programs or for cases where there are only a few concurrent i/o
operations running at any one time then native threads are fine.  But
using native OS threads with blocking i/o on i/o-bound workloads does
not scale.  No one writes a server these days with one thread per
connection, which would be required for your blocking i/o: instead you
multiplex connections with poll/epoll/kqueue and tune the number of
threads to the workload and the number of cores available.  You can have
millions of C++20-style stackless coroutines running at any one time
on a thread, although you are going to run out of file descriptors
before any such limit is reached (I see that my relatively modest 8 core
machine has a hard maximum of 805038 open descriptors although other
things would probably break before then).

> Coroutines are not going to be better at this, either.
> 
> > If you use native OS threads for i/o bound workloads, you are doing it
> > wrong.
> 
> Nope. One only needs to understand that I/O bound workloads is not the only  
> reason that threads exist.

Clueless.

[toc] | [prev] | [next] | [standalone]


#67282

FromSam <sam@email-scan.com>
Date2019-08-04 16:43 -0400
Message-ID<cone.1564951432.763732.27147.1004@monster.email-scan.com>
In reply to#67274

[Multipart message — attachments visible in raw view] — view raw

Chris Vine writes:

>
> You have it wrong.
>
> For toy programs or for cases where there are only a few concurrent i/o
> operations running at any one time then native threads are fine.  But
> using native OS threads with blocking i/o on i/o-bound workloads does
> not scale.

Says who?

>            No one writes a server these days with one thread per
> connection, which would be required for your blocking i/o: instead you
> multiplex connections with poll/epoll/kqueue and tune the number of
> threads to the workload and the number of cores available.

Maybe you just don't know many people who write server code.

> You can have
> millions of C++20-style stackless coroutines running at any one time
> on a thread, although you are going to run out of file descriptors
> before any such limit is reached

Sure, and since coroutines introduce an additional level of complexity –  
something also needs to be aware and keep track of which coroutine has  
something that needs to be done, instead of efficiently encapsulating all  
I/O handling logic in a simple, straightforward, execution thread; and no  
other part of the code needs to give a shit – it still unclear what problem  
they're trying to solve.

Modern operating systems have no problems scaling to thousands, and maybe  
even tens of thousands of execution threads. Perhaps you don't have much  
exposure to them, and are only exposed to technically flawed operating  
system, with utterly crappy thread implementations?

> > > If you use native OS threads for i/o bound workloads, you are doing it
> > > wrong.
> >
> > Nope. One only needs to understand that I/O bound workloads is not the only
> > reason that threads exist.
>
> Clueless.

As opposed to someone who religiously believes that the only reason one  
someone would use threads is in CPU-bound code?

Maybe you should talk to Chrome and Firefox folks, and teach them how to  
write code, since you know so much about it, and explain how stupid they  
are, creating dozens of threads even when most of their load is I/O bound.

[toc] | [prev] | [next] | [standalone]


#67283

From"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>
Date2019-08-04 13:58 -0700
Message-ID<qi7gt6$ct1$1@gioia.aioe.org>
In reply to#67282
On 8/4/2019 1:43 PM, Sam wrote:
> Chris Vine writes:
> 
>>
>> You have it wrong.
>>
>> For toy programs or for cases where there are only a few concurrent i/o
>> operations running at any one time then native threads are fine.  But
>> using native OS threads with blocking i/o on i/o-bound workloads does
>> not scale.
> 
> Says who?
> 
>>            No one writes a server these days with one thread per
>> connection, which would be required for your blocking i/o: instead you
>> multiplex connections with poll/epoll/kqueue and tune the number of
>> threads to the workload and the number of cores available.
> 
> Maybe you just don't know many people who write server code.

I remember using IOCP back on WinNT 4. The idea of a thread per 
connection simply does not scale. However, creating a thread or two per 
CPU, and using a queue, like IOCP can scale.


> 
>> You can have
>> millions of C++20-style stackless coroutines running at any one time
>> on a thread, although you are going to run out of file descriptors
>> before any such limit is reached
> 
> Sure, and since coroutines introduce an additional level of complexity – 
> something also needs to be aware and keep track of which coroutine has 
> something that needs to be done, instead of efficiently encapsulating 
> all I/O handling logic in a simple, straightforward, execution thread; 
> and no other part of the code needs to give a shit – it still unclear 
> what problem they're trying to solve.
> 
> Modern operating systems have no problems scaling to thousands, and 
> maybe even tens of thousands of execution threads. Perhaps you don't 
> have much exposure to them, and are only exposed to technically flawed 
> operating system, with utterly crappy thread implementations?
> 
>> > > If you use native OS threads for i/o bound workloads, you are 
>> doing it
>> > > wrong.
>> >
>> > Nope. One only needs to understand that I/O bound workloads is not 
>> the only
>> > reason that threads exist.
>>
>> Clueless.
> 
> As opposed to someone who religiously believes that the only reason one 
> someone would use threads is in CPU-bound code?
> 
> Maybe you should talk to Chrome and Firefox folks, and teach them how to 
> write code, since you know so much about it, and explain how stupid they 
> are, creating dozens of threads even when most of their load is I/O bound.
> 

[toc] | [prev] | [next] | [standalone]


#67286

FromSam <sam@email-scan.com>
Date2019-08-04 17:36 -0400
Message-ID<cone.1564954572.633096.27147.1004@monster.email-scan.com>
In reply to#67283

[Multipart message — attachments visible in raw view] — view raw

Chris M. Thomasson writes:

> On 8/4/2019 1:43 PM, Sam wrote:
>>> multiplex connections with poll/epoll/kqueue and tune the number of
>>> threads to the workload and the number of cores available.
>>
>> Maybe you just don't know many people who write server code.
>
> I remember using IOCP back on WinNT 4. The idea of a thread per connection  
> simply does not scale. However, creating a thread or two per CPU, and using  
> a queue, like IOCP can scale.

I agree that "a thread per connection simply does not scale" on Microsoft  
Windows. However that's only true for Microsoft Windows, and its crap  
multithreading.

A thread per connection scales perfectly fine, on Linux. Even with hundreds  
of connections. Thread overhead is quite negligible on Linux, and you end up  
writing clean, orderly code that runs logically from start to finish,  
instead of getting torn into shreds in order to conform to event loop or  
callback-based design patterns.

[toc] | [prev] | [next] | [standalone]


#67287

FromMr Flibble <flibbleREMOVETHISBIT@i42.co.uk>
Date2019-08-04 22:43 +0100
Message-ID<jkI1F.72700$Bd4.70055@fx26.fr7>
In reply to#67286
On 04/08/2019 22:36, Sam wrote:
> Chris M. Thomasson writes:
> 
>> On 8/4/2019 1:43 PM, Sam wrote:
>>>> multiplex connections with poll/epoll/kqueue and tune the number of
>>>> threads to the workload and the number of cores available.
>>>
>>> Maybe you just don't know many people who write server code.
>>
>> I remember using IOCP back on WinNT 4. The idea of a thread per 
>> connection simply does not scale. However, creating a thread or two per 
>> CPU, and using a queue, like IOCP can scale.
> 
> I agree that "a thread per connection simply does not scale" on Microsoft 
> Windows. However that's only true for Microsoft Windows, and its crap 
> multithreading.
> 
> A thread per connection scales perfectly fine, on Linux. Even with 
> hundreds of connections. Thread overhead is quite negligible on Linux, and 
> you end up writing clean, orderly code that runs logically from start to 
> finish, instead of getting torn into shreds in order to conform to event 
> loop or callback-based design patterns.

Nonsense. You should have no more than one logical thread per physical 
thread for particular task type.

/Flibble

-- 
"Snakes didn't evolve, instead talking snakes with legs changed into 
snakes." - Rick C. Hodgin

“You won’t burn in hell. But be nice anyway.” – Ricky Gervais

“I see Atheists are fighting and killing each other again, over who 
doesn’t believe in any God the most. Oh, no..wait.. that never happens.” – 
Ricky Gervais

"Suppose it's all true, and you walk up to the pearly gates, and are 
confronted by God," Bryne asked on his show The Meaning of Life. "What 
will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery 
that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a 
world that is so full of injustice and pain. That's what I would say."

[toc] | [prev] | [next] | [standalone]


#67289

FromChris Vine <chris@cvine--nospam--.freeserve.co.uk>
Date2019-08-04 22:52 +0100
Message-ID<20190804225258.6421be3b8cacabe051da4015@cvine--nospam--.freeserve.co.uk>
In reply to#67286
On Sun, 04 Aug 2019 17:36:12 -0400
Sam <sam@email-scan.com> wrote:
> Chris M. Thomasson writes:
> 
> > On 8/4/2019 1:43 PM, Sam wrote:
> >>> multiplex connections with poll/epoll/kqueue and tune the number of
> >>> threads to the workload and the number of cores available.
> >>
> >> Maybe you just don't know many people who write server code.
> >
> > I remember using IOCP back on WinNT 4. The idea of a thread per connection  
> > simply does not scale. However, creating a thread or two per CPU, and using  
> > a queue, like IOCP can scale.
> 
> I agree that "a thread per connection simply does not scale" on Microsoft  
> Windows. However that's only true for Microsoft Windows, and its crap  
> multithreading.
> 
> A thread per connection scales perfectly fine, on Linux. Even with hundreds  
> of connections. Thread overhead is quite negligible on Linux, and you end up  
> writing clean, orderly code that runs logically from start to finish,  
> instead of getting torn into shreds in order to conform to event loop or  
> callback-based design patterns.

I disagree.  And "hundreds of connections" is not good enough.

[toc] | [prev] | [next] | [standalone]


#67290

FromMr Flibble <flibbleREMOVETHISBIT@i42.co.uk>
Date2019-08-04 23:10 +0100
Message-ID<6JI1F.54517$U17.3799@fx28.fr7>
In reply to#67289
On 04/08/2019 22:52, Chris Vine wrote:
> On Sun, 04 Aug 2019 17:36:12 -0400
> Sam <sam@email-scan.com> wrote:
>> Chris M. Thomasson writes:
>>
>>> On 8/4/2019 1:43 PM, Sam wrote:
>>>>> multiplex connections with poll/epoll/kqueue and tune the number of
>>>>> threads to the workload and the number of cores available.
>>>>
>>>> Maybe you just don't know many people who write server code.
>>>
>>> I remember using IOCP back on WinNT 4. The idea of a thread per connection
>>> simply does not scale. However, creating a thread or two per CPU, and using
>>> a queue, like IOCP can scale.
>>
>> I agree that "a thread per connection simply does not scale" on Microsoft
>> Windows. However that's only true for Microsoft Windows, and its crap
>> multithreading.
>>
>> A thread per connection scales perfectly fine, on Linux. Even with hundreds
>> of connections. Thread overhead is quite negligible on Linux, and you end up
>> writing clean, orderly code that runs logically from start to finish,
>> instead of getting torn into shreds in order to conform to event loop or
>> callback-based design patterns.
> 
> I disagree.  And "hundreds of connections" is not good enough.

You are correct to disagree. Sam has obviously never heard of "epoll".

/Flibble

-- 
"Snakes didn't evolve, instead talking snakes with legs changed into 
snakes." - Rick C. Hodgin

“You won’t burn in hell. But be nice anyway.” – Ricky Gervais

“I see Atheists are fighting and killing each other again, over who 
doesn’t believe in any God the most. Oh, no..wait.. that never happens.” – 
Ricky Gervais

"Suppose it's all true, and you walk up to the pearly gates, and are 
confronted by God," Bryne asked on his show The Meaning of Life. "What 
will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery 
that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a 
world that is so full of injustice and pain. That's what I would say."

[toc] | [prev] | [next] | [standalone]


#67293

FromSam <sam@email-scan.com>
Date2019-08-04 21:32 -0400
Message-ID<cone.1564968742.615770.1074.1004@monster.email-scan.com>
In reply to#67289

[Multipart message — attachments visible in raw view] — view raw

Chris Vine writes:

> On Sun, 04 Aug 2019 17:36:12 -0400
> Sam <sam@email-scan.com> wrote:
>
> > I agree that "a thread per connection simply does not scale" on Microsoft
> > Windows. However that's only true for Microsoft Windows, and its crap
> > multithreading.
> >
> > A thread per connection scales perfectly fine, on Linux. Even with hundreds
> > of connections. Thread overhead is quite negligible on Linux, and you end  
> up
> > writing clean, orderly code that runs logically from start to finish,
> > instead of getting torn into shreds in order to conform to event loop or
> > callback-based design patterns.
>
> I disagree.  And "hundreds of connections" is not good enough.

You can disagree all you want. Historical facts prove otherwise. Since  
ancient days, the simplest task on Unixes were done as pipelined tasks, as  
multiple execution contexts.

$ sort <file | uniq -c

That dates back decades.From the earlier days, shells were explicitly  
designed to execute pipelined commands. Pipelined commands were their main  
feature. All of this is pretty much I/O bound. Multiple execution threads.

Because of that, and battle-tested over decades, Unix was tuned to  
effortlessly implement tasks that use multiple processes working in  
parallel. Threads were just the next evolution, dropping most of what little  
was left, in terms of per-execution context overhead. Linux inherited Unix's  
legacy, and orientation, towards low-overhead multiple execution threads.

All the historical network server processes on Unix, then Linux, were multi- 
processor based. Even though select() existed pretty much since 4.2 BSD  
days, it was virtually unheard of for a network server to be a monolithic  
process, multiplexing for its clients using select(). An incoming network  
connection starts a new process, just for that network connection. Didn't  
matter what network service it was. SMTP, finger, login, or what, it was all  
one process per connection.

There was a reason for that. It didn't take an Einstein to figure out that  
with a monolithic server, once it picked up an I/O event it had to do what  
that event needs to do, and nothing else can happen until that's done. Even  
if another client blurted out a packet, too bad, so sad. It needs to wait  
until the current I/O event's processing is done. It made a lot more sense  
to simply have another available CPU, not being busy with anything, run with  
it. And, goshdarndammit, if that socket had a different execution thread  
sucking from it, why, that spare CPU will know exactly what to do.

So, monolithic processes were rare. They are a little bit more common today  
than before, but they are still a rarity. And, today, even network servers  
that kick off multiple processes per client can be found. So, sorry, but  
facts disagree: multiple execution contexts, either as standalone processes  
or lightweight intra-process threads rule the roost in I/O bound contexts.  
Linux inherits Unix's legacy in this respect, and the differences between  
processes and threads, on Linux, are mostly cosmetic. clone() creates both  
of them. You just specify which part of the parent process (virtual memory  
map, file descriptors, filesystem namespace etc…) are shared with the new  
execution context, and that's pretty much.

I can understand why coming from a MS-Windows background makes someone frown  
at execution threads, unless all they do is stay away from the operating  
system, and work entirely on their own, spinning the CPU. The only reason  
stuff like IOCP exists on Windows is because windows sucks at multi- 
threading and multi-processing. It always sucked, and will likely always  
suck.

But that's not the case with the real world out there. Being able to have a  
clean, encapsulated, logical execution thread, always busy with the forward  
march of progress from the start pointing to the finish line, without being  
forced into contorting into an event/dispatch based framework, results in  
smaller, leaner code without all that event-based/dispatching cruft. It  
doesn't matter which one of the file descriptors is now ready for I/O. It no  
longer matters. The OS kernel already figured it out, and there's no good  
reason for userspace to piss away more CPU cycles doing exactly the same  
thing. The OS kernel knows which I/O is done, and which thread is waiting on  
it, and it already knows whose ass to kick into gear.

A such, execution threads are perfect for I/O-based load. It is no wonder  
that Linux rules the roost in TOP500 space. It's a dirty little secret that  
all those supercomputers are just a bunch of machines with optimized  
networking tying hundreds of thousands threads together. Yes, threads.

Gee whiz, how can that possibly happen?


[toc] | [prev] | [next] | [standalone]


Page 1 of 4  [1] 2 3 4  Next page →

Back to top | Article view | comp.lang.c++


csiph-web