Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (475)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (530)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Selector.select() returns 0 during connected clients  (Read 6012 times)
0 Members and 1 Guest are viewing this topic.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Posted 2008-09-15 00:45:56 »

First of all, I'm not using selector.wakeup() or Thread.interrupt().
Selector.select() is supposed to block, until "at least one channel is selected".
This behaviour is clearly breaking the contract defined in the Javadoc.



The thing I (might) do out of the ordinary, is that I keep changing the 'interest ops' of OP_WRITE, so that after the bytes are written, the OP_WRITE is not part of the interest ops anymore, and it gets activated again when i have data available.

All code is in the same Thread, so multi-threading issues are out of the question.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
   private LinkedList<byte[]> queue;

   public void enqueue(SelectionKey key, byte[] buf)
   {
      queue.addLast(buf);
      this.adjustInterestOps(key, OP_WRITE, true);
   }

   public void writeQueue(SelectionKey key)
   {
      if(queue.isEmpty())
      {
         this.adjustInterestOps(key, OP_WRITE, false);
         return;  
      }

      byte[] data = queue.removeFirst();
      // write to SocketChannel
  }

   private final void adjustInterestOp(SelectionKey key, int op, boolean state)
   {
     try
      {
         int ops = key.interestOps();
         if (state != ((ops & op) == op))
            key.interestOps(state ? (ops | op) : (ops & ~op));
      }
      catch (CancelledKeyException exc)
      {
         // ignore
     }
   }


So what happens is that the code works perfectly fine, except that Selector.select() returns 0 quite often, which is pounding on my CPU (and again: breaking the contract of NIO). When there *are* events, like new clients, and read/write events, it works like normal, returning 1 or a value slightly higher.

I put a sleep(100) inthere now, if select() returns 0, but could anybody please explain to me why this code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
while(true)
{
    int selected = selector.select();
    System.out.println("selected: "+selected);

      keys = selector.selectedKeys().iterator();
      while (keys.hasNext())
      {
         SelectionKey key = keys.next();
         keys.remove();
         ........
      }
}


shows this behaviour:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
selected: 1 // client connects
selected: 0
selected: 0
selected: 0
selected: 1 // client receives data
selected: 0
selected: 0
selected: 0
selected: 1 // client (ready to) write data
selected: 0
selected: 0
selected: 0
selected: 0
...
selected: 0
selected: 1 // client disconnects
// no more prints here... selector.select() now blocks after the last client disconnects

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Mr_Light

Senior Member




shiny.


« Reply #1 - Posted 2008-09-15 03:11:07 »

First of all, I'm not using selector.wakeup() or Thread.interrupt().
Selector.select() is supposed to block, until "at least one channel is selected".

While you might not be using those methods are you sure it doesn't get invoked indirectly, have you run your code though the debugger?

esp because it does block later on, where all the clients are disconnected.

It's harder to read code than to write it. - it's even harder to write readable code.

The gospel of brother Riven: "The guarantee that all bugs are in *your* code is worth gold." Amen brother a-m-e-n.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #2 - Posted 2008-09-15 07:49:40 »

All code in the app is mine. There is one thread. It's a pretty small piece of code.

I'm 100% sure I'm not calling wakeup nor calling Thread.interrupt().


I'll try to extract a self contained testcase out of it, which I'll run through a profiler, and post it here later today.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Addictman

Senior Member


Medals: 3
Projects: 1


Java games rock!


« Reply #3 - Posted 2008-09-15 09:49:06 »

Hi.

I just tested my server code, and my selector.select()  never returns 0. I'm not sure why you're modifying your OP codes this way though, and I guess that has something to do with your bug.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #4 - Posted 2008-09-15 10:00:39 »

If you have OP_WRITE 'enabled', it keeps pumping (basically selector.select() returns 1 immediately) that the channel is 'ready to write', when I don't have any data, so I disable OP_WRITE after I have written everything I have to the channel.

When there is new data to write, I enable the OP_WRITE again, so I get notified when the channel is ready to write.



It's really a nice approach to prevent getting swamped with 'ready to write' channels that keep firing.



To summarize:
 When OP_WRITE is turned ON all the time: select() immediately returns 1, as long as I can write.
 When OP_WRITE is TOGGLED depending on queue size: select() immediately returns 0, as long as I can write.


To me it looks like the OP_WRITE is still activated in the OS level, but Java filters out the SelectionKeys from the Set as it does not match the interestOps.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Addictman

Senior Member


Medals: 3
Projects: 1


Java games rock!


« Reply #5 - Posted 2008-09-15 11:38:21 »

Do you use this because you're running everything on a single thread, or because you find it to generally offer the best performance? It's been a while since I did any serious java.nio coding, but I seem to remember I used a wait/notify mechanism on the writable channels. I guess perhaps that's not an option in your case.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #6 - Posted 2008-09-15 12:22:25 »

Using wait/notify on channels is kinda defeating the whole purpose of NIO.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Addictman

Senior Member


Medals: 3
Projects: 1


Java games rock!


« Reply #7 - Posted 2008-09-15 12:39:21 »

I don't see how, really. If select blocks properly until there is something to write (like it is supposed to, but obviously doesnt?), then there's not much difference. A slight overhead perhaps, but nothing really noticable for me so far at least. However, I feel like I have more control over when I am sending data and not. In a turn based game, I was pushing data into the channel, but only when a turn ended did I actually want to send it. Like I said, it's been a long time, but I distinctly remember it all had a purpose Smiley

If what I wrote is unclear, I'll clarify that I weren't using any wait/notify regimes on the receiving of data, of course. I always felt that it was in this part that nio had its nonblocking purpose as upposed to standard io. But oh well. Good luck in solving your problem anyway! Smiley
Offline Mr_Light

Senior Member




shiny.


« Reply #8 - Posted 2008-09-16 01:40:34 »

Use two selectors? nio has been ages for me too, well more like years  Roll Eyes I don't know I can't remember using any kind of mechanism like that. Wild guess you sure changing OP_WRITE doesn't wake up the selector indirectly/automaticly, now because select returns 0 instead of blocking it executes the logic (it concludes there is nothing to write(again/still) sets OP_WRITE again and it starts over.

It's harder to read code than to write it. - it's even harder to write readable code.

The gospel of brother Riven: "The guarantee that all bugs are in *your* code is worth gold." Amen brother a-m-e-n.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #9 - Posted 2008-09-19 20:54:10 »

Can't be the cause, because there is only 1 thread.

OP_WRITE is changed *during* the traversal of the selectedKeys iterator.

I haven't found time to make a testcase yet. I've been way too busy with work, and the Thread.sleep(10) is an OK solution for now.

As soon as I have some time (maybe this weekend) I'll post the testcase.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #10 - Posted 2008-09-24 10:15:23 »

1. Wait/notify on Channels? WTF? Smiley What were you doing, precisely, with wait/notify? They *are* necessary, but not for selection operations - rather for the fact that most operations on selectors and selectionkeys block, often in unexepcted places and ways, so that if you have extra threads accessing, you MUST use some form of synchronization.

Riven's attempted approach - dynamically adjusting the OP sets - is precisely what you are supposed to do.

2. Riven - What's the actual problem here? The thread topic you've made is precisely as described in the javadocs:

"The number of keys, possibly zero, whose ready-operation sets were updated "

?

What's in your selected-key sets?

On a different note, your adjustInterestOp method is quite confusing, I think it works but I'd appreciate some explanation.

Firstly, doing an if compare against false not being equal to a bitwise compare is IMHO impressively obscure, and I'm not convinced its doing precisely what you want it to be doing (but I'm just guessing what I *think* you want it to be doing, I'm really not sure!)

Secondly, setting interestops using anythign other than direct combinations of OP_WRITE, OP_READ, OP_CONNECT is often a source of stupid bugs. Odd combinations of bits in the interestops have caused some very weird behaviour in apps I've seen in the past. Since you're (I believe) trying to remove an op, you need to be doubly careful. c.f. above point about clarity.

malloc will be first against the wall when the revolution comes...
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #11 - Posted 2008-09-24 11:33:06 »

From the Javadocs:
Quote
This method performs a blocking selection operation. It returns only after at least one channel is selected, this selector's wakeup method is invoked, or the current thread is interrupted, whichever comes first.

So if nobody is calling selector.wakeup() or calling Thread.interrupt(), select() MUST only return with a non zero return value. (!)




Quote
"The number of keys, possibly zero, whose ready-operation sets were updated "

?

What's in your selected-key sets?

Empty.



Quote
On a different note, your adjustInterestOp method is quite confusing, I think it works but I'd appreciate some explanation.

Ah, just a bit of bit-shuffling fun.

If you 'de-inline' the source-code, it'll become obvious:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
   private final void adjustInterestOp(SelectionKey key, int op, boolean state)
   {
         int ops = key.interestOps();
         if (state != ((ops & op) == op))
            key.interestOps(state ? (ops | op) : (ops & ~op));
   }

   // un-inline some
  private final void adjustInterestOp(SelectionKey key, int op, boolean state)
   {
         int ops = key.interestOps();
         int maskOpInInterestOps = (ops & op);
         boolean isOpInInterestedOps = (maskOpInInterestOps == op);
         if(state == isOpInInterestedOps)
            return;
         key.interestOps(state ? (ops | op) : (ops & ~op));
   }

   // un-inline further
  private final void adjustInterestOp(SelectionKey key, int op, boolean state)
   {
         int ops = key.interestOps();
         int maskOpInInterestOps = (ops & op);
         boolean isOpInInterestedOps = (maskOpInInterestOps == op);

         // 'state' is whether we want to add or remove the OP
        if(state == isOpInInterestedOps)
            return; // the OP is already in the desired state

          if(state) // enable, because it is disabled
             key.interestOps(ops | op); // add the OP bit to OPS
         else // disable, because it is enabled
            key.interestOps(ops & ~op); // remove the OP bit from OPS
  }


Obviously I wrote the last version first, and thought it would be 'fun' to get it as small as possible - which isn't really what one should be after in production code persecutioncomplex but this piece of code was small enough to remain sure it kept working. All my other code is wildly OO and highly abstracted to keep it all maintainable





Quote
Secondly, setting interestops using anythign other than direct combinations of OP_WRITE, OP_READ, OP_CONNECT is often a source of stupid bugs. Odd combinations of bits in the interestops have caused some very weird behaviour in apps I've seen in the past. Since you're (I believe) trying to remove an op, you need to be doubly careful. c.f. above point about clarity.

So when I remove a bit from the 'set', all other bits are untouched, and when I add a bit, all previous bits are untouched.

I'm sure the 'obfuscated' code is correct (as the 'de-inlined' version proofs).



Again, sorry for not providing a testcase. I'll do so when I have time. Currently I'm extremely busy with work, even at nights and weekends. Sad

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #12 - Posted 2008-09-24 15:11:12 »

Figured it out...

1  
2  
3  
4  
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.socket().setTcpNoDelay(true);
return socketChannel.register(selector,  OP_READ | OP_CONNECT);


After this, the selector keeps returning 0.

Yes, I know persecutioncomplex it doesn't make ANY sense to register OP_CONNECT to an accept()-ed SocketChannel.

But the resulting behavior is kinda non-obvious. Angry


Oh, and it is a bug in both my code and NIO!

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline sunsett

Senior Member




ribbit!


« Reply #13 - Posted 2008-09-24 18:39:24 »

Oh, and it is a bug in both my code and NIO!

At least it's partially your fault, that's what counts. Wink
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #14 - Posted 2008-10-02 12:30:11 »

Were you finsihing the connect() mannually?

(and you ought to post a LOT more source code if you want help with things like this Smiley)

e.g. in my original reply, I almost just wrote something like:

"I bet you've got a screwed up use of OP_ codes somewhere, that usually causes strange things to happen, and you're already making them confusing in the bit of your code you posted here - that's not a good sign"

Smiley...but I thought that would be too rude, and - without any of your source to look at - making massive assumptions that might be grossly unfair. Apart from the one about your bit fiddling (which is what gave me the impression you might not be beign careful enough wiht them ... somehwere .. in your code).

If you'd posted all the code that was related to the problem (selector, schannel, skey), might have got a quicker answer Smiley.

malloc will be first against the wall when the revolution comes...
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 742
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #15 - Posted 2008-10-02 19:05:53 »

It's not about a quick reply, I had a workaround in 5 minutes after I found the bug (it still is a bug in NIO!)


You can argue that passing arguments that makes no sense resulting in a method breaking contract, is no bug, but IMHO it is.

Anyway, I didn't screwup when adjusting the OPs, just when the OP was set at the first time (in the selector.register(...) method).



Anyway, thanks for your feedback, it's not rude, I'm not offended, I just feel the screwup was only partly my fault, and partly to be blamed on NIO.


And as you may have noticed in my replies, I said I was way to busy to narrow it down to a testcase. Well... when I found the time to narrow it down, I found the bug (duh), so I didn't feel any urge to post my testcase after that.


The original code that had the bug is rather big, and I'm not sure I can just dump it on this forum, as I wrote (most of) it at work.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Riven (4 views)
2014-07-23 21:16:32

Riven (6 views)
2014-07-23 21:07:15

Riven (6 views)
2014-07-23 20:56:16

ctomni231 (40 views)
2014-07-18 06:55:21

Zero Volt (36 views)
2014-07-17 23:47:54

danieldean (30 views)
2014-07-17 23:41:23

MustardPeter (32 views)
2014-07-16 23:30:00

Cero (47 views)
2014-07-16 00:42:17

Riven (48 views)
2014-07-14 18:02:53

OpenGLShaders (38 views)
2014-07-14 16:23:47
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!