Mono is Dead
- 13. 同期版
TcpClient client = // Accept部分は省略
Stream stream = client.GetStream();
while (true) {
var buf = new buf[5];
stream.Read(buf, 0, buf.Length);
stream.Write(buf, 0, buf.Length);
}
- 15. 同期版(改)
static void ReadFully(this Stream stream, byte[] buf) {
var readBytes = 0;
while (readBytes != buf.Length) {
var n = stream.Read(buf, readBytes,
buf.Length - readBytes);
if (n == 0)
throw new Exception("hoge-");
readBytes += n;
}
}
- 16. 同期版(改)
try {
while (true) {
var buf = new buf[5];
stream.ReadFully(buf);
stream.Write(buf, 0, buf.Length);
}
} catch (Exception e) {
// 例外出たらログを残して接続を閉じる
logger.Error("error", e);
client.Close();
}
- 24. 同期版(再掲)
try {
while (true) {
var buf = new buf[5];
stream.ReadFully(buf);
stream.Write(buf, 0, buf.Length);
}
} catch (Exception e) {
// 例外出たらログを残して接続を閉じる
logger.Error("error", e);
client.Close();
}
- 25. 非同期版
var buf = new byte[5];
Func<IAsyncResult> func;
func = ar1 => {
stream.EndReadFully(ar1);
stream.BeginWrite(buf, 0, buf.Length, ar2 => {
stream.EndWrite(ar2);
// 再度BeginReadを始める(whileループ相当)
stream.BeginReadFully(buf, 0, buf.Length, func,
null);
};
};
stream.BeginReadFully(buf, 0, buf.Length, func, null);
完全に別コード
- 26. 非同期版
var buf = new byte[5];
Func<IAsyncResult> func;
func = ar1 => {
stream.EndReadFully(ar1);
stream.BeginWrite(buf, 0, buf.Length, ar2 => {
stream.EndWrite(ar2);
// 再度BeginReadを始める(whileループ相当)
stream.BeginReadFully(buf, 0, buf.Length, func,
null);
};
};
stream.BeginReadFully(buf, 0, buf.Length, func, null);
どうやって実装するのか分かりませんでした
- 27. 非同期版
var buf = new byte[5];
Func<IAsyncResult> func;
func = ar1 => {
stream.EndReadFully(ar1); // 例外処理どうしよう
stream.BeginWrite(buf, 0, buf.Length, ar2 => {
stream.EndWrite(ar2); // 例外処理どうしよう
// 再度BeginReadを始める(whileループ相当)
stream.BeginReadFully(buf, 0, buf.Length, func,
null);
};
};
stream.BeginReadFully(buf, 0, buf.Length, func, null);
例外処理つらい
- 28. 非同期版
var buf = new byte[5];
Func<IAsyncResult> func;
func = ar1 => {
stream.EndReadFully(ar1);
stream.BeginWrite(buf, 0, buf.Length, ar2 => {
stream.EndWrite(ar2);
// 再度BeginReadを始める(whileループ相当)
stream.BeginReadFully(buf, 0, buf.Length, func,
null);
};
};
stream.BeginReadFully(buf, 0, buf.Length, func, null);
whileループすら再帰とか…
- 31. 同期版(再掲)
static void ReadFully(
this Stream stream, byte[] buf) {
var readBytes = 0;
while (readBytes != buf.Length) {
var n = stream.Read(buf, readBytes,
buf.Length - readBytes);
if (n == 0)
throw new Exception("hoge-");
readBytes += n;
}
}
- 32. async/await版
static async Task ReadFullyAsync(
this Stream stream, byte[] buf) {
var readBytes = 0;
while (readBytes != buf.Length) {
var n = await stream.ReadAsync(buf, readBytes,
buf.Length - readBytes)
.ConfigureAwait(false);
if (n == 0)
throw new Exception("hoge-");
readBytes += n;
};
}
- 33. 差分
static async Task ReadFullyAsync(
this Stream stream, byte[] buf) {
var readBytes = 0;
while (readBytes != buf.Length) {
var n = await stream.ReadAsync(buf, readBytes,
buf.Length - readBytes)
.ConfigureAwait(false);
if (n == 0)
throw new Exception("hoge-");
readBytes += n;
};
}
- 34. 同期版(再掲)
try {
while (true) {
var buf = new buf[5];
stream.ReadFully(buf);
stream.Write(buf, 0, buf.Length);
}
} catch (Exception e) {
// 例外出たらログを残して接続を閉じる
logger.Error("error", e);
client.Close();
}
- 35. async/await版
try {
while (true) {
var buf = new buf[5];
await stream.ReadFullyAsync(buf)
.ConfigureAwait(false);
await stream.WriteAsync(buf, 0, buf.Length)
.ConfigureAwait(false);
}
} catch (Exception e) {
// 例外出たらログを残して接続を閉じる
logger.Error("error", e);
client.Close();
}
- 36. 差分
try {
while (true) {
var buf = new buf[5];
await stream.ReadFullyAsync(buf)
.ConfigureAwait(false);
await stream.WriteAsync(buf, 0, buf.Length)
.ConfigureAwait(false);
}
} catch (Exception e) {
// 例外出たらログを残して接続を閉じる
logger.Error("error", e);
client.Close();
}
- 39. 実際の動き
try {
while (true) {
var buf = new buf[5];
await stream.ReadFullyAsync(buf)
.ConfigureAwait(false);
await stream.WriteAsync(buf, 0, buf.Length)
.ConfigureAwait(false);
}
} catch (Exception e) {
// 例外出たらログを残して接続を閉じる
logger.Error("error", e);
client.Close();
}
Thread1
Thread2
- 49. Mono is Dead
• VC#ではうまく動いていたexeをMonoで
実行すると…
_人人人人人人_
> 突然の死 <
 ̄Y^Y^Y^Y^Y ̄
- 53. LogicalSetData で死ぬ
[ThreadStatic] static Hashtable logicalDatastore;
static void LogicalSetData(string name, object data)
{
var r = logicalDatastore;
if (r == null)
r = logicalDatastore = new Hashtable();
r[name] = data;
}
Mono-3.2.8のソースより
- 54. LogicalSetData で死ぬ
[ThreadStatic] static Hashtable logicalDatastore;
static void LogicalSetData(string name, object data)
{
var r = logicalDatastore;
if (r == null)
r = logicalDatastore = new Hashtable();
r[name] = data;
}
ただのTLS実装になっている
そんな実装で大丈夫か?
Mono-3.2.8のソースより
- 60. キャンセルトークンで死ぬ
public virtual Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken)
// Task1
CancellationToken token = tokenSource.Token;
await stream.ReadAsync(buf, 0, size, token);
// Task2
// ReadAsyncの処理を中断させる
tokenSource.Cancel();
- 64. キャンセルトークンで死ぬ
static async Task TestConnect() {
try {
var client = new TcpClient();
var task = Task.Run(
() => client.ConnectAsync("localhost", 8080));
client.Close();
await task.ConfigureAwait(false);
} catch (Exception) { }
}
これを10万回ぐらい呼び出すと大体死ぬ
全体コード
- 68. キャンセルトークンで死ぬ
public async Task<int> ReadAsync(
byte[] buf, int offset, int length,
CancellationToken token) {
// リクエストキューに詰めて
await requestQueue.Enqueue(new Request() {
Type = Operation.ReadAsync,
Buf = buf,
Offset = offset,
Length = length,
}).ConfigureAwait(false);
// レスポンスキューに結果が返ってくるのを待つ
var resp = await responseQueue.Dequeue(token)
.ConfigureAwait(false);
return resp.ReadResult;
}
- 69. キャンセルトークンで死ぬ
public async Task<int> ReadAsync(
byte[] buf, int offset, int length,
CancellationToken token) {
// リクエストキューに詰めて
await requestQueue.Enqueue(new Request() {
Type = Operation.ReadAsync,
Buf = buf,
Offset = offset,
Length = length,
}).ConfigureAwait(false);
// レスポンスキューに結果が返ってくるのを待つ
var resp = await responseQueue.Dequeue(token)
.ConfigureAwait(false);
return resp.ReadResult;
}
responseQueueがCancellationTokenに対応してればいい
- 70. キャンセルトークンで死ぬ
async void Run() {
while (true) {
// リクエストを受け取り
var op = await requestQueue.Dequeue().ConfigureAwait(false);
// リクエスト毎の処理をして
switch (op.Type) {
case Operation.ReadAsync:
var result = await client.ReadAsync(
op.Buf, op.Offset, op.Length).ConfigureAwait(false);
// レスポンスを返す
await responseQueue.Enqueue(resp)
.ConfigureAwait(false);
break;
case Operation.WriteAsync:
...
}
}
- 76. BufferBlockで死ぬ
public async Task<int> ReadAsync(
byte[] buf, int offset, int length,
CancellationToken token) {
// リクエストキューに詰めて
await requestQueue.Enqueue(new Request() {
Type = Operation.ReadAsync,
Buf = buf,
Offset = offset,
Length = length,
}).ConfigureAwait(false);
// レスポンスキューに結果が返ってくるのを待つ
var resp = await responseQueue.Dequeue(token)
.ConfigureAwait(false);
return resp.ReadResult;
}
requestQueueとresponseQueueはどうやって作るの?
- 80. BufferBlockで死ぬ
static async Task SendTask(BufferBlock<string> bb) {
for (int i = 0; i < 10000; i++) {
await bb.SendAsync(i.ToString()).ConfigureAwait(false);
await Task.Delay(1).ConfigureAwait(false);
}
}
static async Task ReceiveTask(BufferBlock<string> bb) {
for (int i = 0; i < 10000; i++) {
try {
await bb.ReceiveAsync(TimeSpan.FromMilliseconds(1))
.ConfigureAwait(false);
} catch (Exception) { }
}
}
SendTaskとReceiveTaskを同時に実行すると大体死ぬ
全体コード