Debug toString for channels

Fixes #185
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt
index 16862d1..a42517a 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt
@@ -16,17 +16,11 @@
 
 package kotlinx.coroutines.experimental.channels
 
-import kotlinx.coroutines.experimental.CancellableContinuation
-import kotlinx.coroutines.experimental.DisposableHandle
+import kotlinx.coroutines.experimental.*
 import kotlinx.coroutines.experimental.internal.*
-import kotlinx.coroutines.experimental.intrinsics.startCoroutineUndispatched
-import kotlinx.coroutines.experimental.removeOnCancel
-import kotlinx.coroutines.experimental.selects.ALREADY_SELECTED
-import kotlinx.coroutines.experimental.selects.SelectClause1
-import kotlinx.coroutines.experimental.selects.SelectClause2
-import kotlinx.coroutines.experimental.selects.SelectInstance
-import kotlinx.coroutines.experimental.suspendAtomicCancellableCoroutine
-import kotlin.coroutines.experimental.startCoroutine
+import kotlinx.coroutines.experimental.intrinsics.*
+import kotlinx.coroutines.experimental.selects.*
+import kotlin.coroutines.experimental.*
 
 /**
  * Abstract send channel. It is a base class for all send channel implementations.
@@ -374,6 +368,37 @@
         }
     }
 
+    // ------ debug ------
+
+    public override fun toString() =
+        "$classSimpleName@$hexAddress{$queueDebugStateString}$bufferDebugString"
+
+    private val queueDebugStateString: String
+        get() {
+            val head = queue.next
+            if (head === queue) return "EmptyQueue"
+            var result = when (head) {
+                is Closed<*> -> head.toString()
+                is Receive<*> -> "ReceiveQueued"
+                is Send -> "SendQueued"
+                else -> "UNEXPECTED:$head" // should not happen
+            }
+            val tail = queue.prev
+            if (tail !== head) {
+                result += ",queueSize=${countQueueSize()}"
+                if (tail is Closed<*>) result += ",closedForSend=$tail"
+            }
+            return result
+        }
+
+    private fun countQueueSize(): Int {
+        var size = 0
+        queue.forEach<LockFreeLinkedListNode> { size++ }
+        return size
+    }
+
+    protected open val bufferDebugString: String get() = ""
+
     // ------ private ------
 
     private class SendSelect<E, R>(
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayBroadcastChannel.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayBroadcastChannel.kt
index e11237f..5c4fa55 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayBroadcastChannel.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayBroadcastChannel.kt
@@ -16,11 +16,10 @@
 
 package kotlinx.coroutines.experimental.channels
 
-import kotlinx.coroutines.experimental.selects.ALREADY_SELECTED
-import kotlinx.coroutines.experimental.selects.SelectInstance
-import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.locks.ReentrantLock
-import kotlin.concurrent.withLock
+import kotlinx.coroutines.experimental.selects.*
+import java.util.concurrent.*
+import java.util.concurrent.locks.*
+import kotlin.concurrent.*
 
 /**
  * Broadcast channel with array buffer of a fixed [capacity].
@@ -353,4 +352,9 @@
             return result
         }
     }
+
+    // ------ debug ------
+
+    override val bufferDebugString: String
+        get() = "(buffer:capacity=${buffer.size},size=$size)"
 }
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayChannel.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayChannel.kt
index 8ce9008..fd8c79d 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayChannel.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ArrayChannel.kt
@@ -16,10 +16,9 @@
 
 package kotlinx.coroutines.experimental.channels
 
-import kotlinx.coroutines.experimental.selects.ALREADY_SELECTED
-import kotlinx.coroutines.experimental.selects.SelectInstance
-import java.util.concurrent.locks.ReentrantLock
-import kotlin.concurrent.withLock
+import kotlinx.coroutines.experimental.selects.*
+import java.util.concurrent.locks.*
+import kotlin.concurrent.*
 
 /**
  * Channel with array buffer of a fixed [capacity].
@@ -245,4 +244,9 @@
         // then clean all queued senders
         super.cleanupSendQueueOnCancel()
     }
+
+    // ------ debug ------
+
+    override val bufferDebugString: String
+        get() = "(buffer:capacity=${buffer.size},size=$size)"
 }
\ No newline at end of file