List of Signal - text.Baldanders.info
tag:text.Baldanders.info,2021-02-18:/tags
2021-02-18T21:34:25+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
time.Ticker で遊ぶ【Go 1.16 バージョン】
tag:text.Baldanders.info,2021-02-18:/golang/ticker-2/
2021-02-18T12:34:25+00:00
2021-12-04T02:40:05+00:00
素敵なキャンセルライフを(笑)
Spiegel
https://baldanders.info/profile/
<p>ずいぶん前に「<a href="https://text.baldanders.info/golang/ticker/" title="time.Ticker で遊ぶ">time.Ticker で遊ぶ</a>」と言う記事を書いたのだが,先日<a href="https://text.baldanders.info/release/2021/02/go-1_16-is-released/" title="Go 1.16 がリリースされた">リリース</a>された <a href="https://go.dev/">Go</a> 1.16 で <a href="https://golang.org/pkg/os/signal/" title="signal - The Go Programming Language"><code>signal</code></a><code>.NotifyContext()</code> 関数が追加された記念に,これを使った改訂版の記事を書いてみたいと思う。</p>
<p><a href="https://text.baldanders.info/golang/ticker/" title="time.Ticker で遊ぶ">前回</a>と同じくお題は以下の通り。</p>
<ol>
<li>一定周期ごとの処理を行う</li>
<li>Ctrl+C 等の割り込み処理を行う</li>
</ol>
<h2>一定周期ごとの処理を行う</h2>
<p>これは<a href="https://text.baldanders.info/golang/ticker/" title="time.Ticker で遊ぶ">前回</a>の記事をほぼそのまま使いまわそう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// +build run
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">//1秒周期の ticker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stopping ticker..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">ticker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>前回でも説明した通り, <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">defer</a> 構文を使って終了時に <a href="http://golang.org/pkg/time/" title="time - The Go Programming Language"><code>time</code></a><code>.Ticker.Stop()</code> 関数で周期イベントを止めようとしているが,実際には無限ループなので return まで到達しない(笑)</p>
<h2>NotifyContext 関数で SIGNAL を捕まえる</h2>
<p><a href="https://go.dev/">Go</a> では SIGINT や SIGTERM といった OS から送信される <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> をイベントとして <a href="http://golang.org/ref/spec#Channel_types" title="The Go Programming Language Specification - The Go Programming Language">channel</a> に送り込む仕掛けがある(ちなみに Ctrl+C は SIGINT として送られる)。
さらに <a href="https://go.dev/">Go</a> 1.16 では <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> イベントを <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> のキャンセル・イベントとして実装できるようになった。</p>
<p>たとえば,こんな感じに書ける。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// +build run
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"context"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os/signal"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">//1秒周期の ticker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stopping ticker..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"cancellation from context:"</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">())</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">ctx</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nf">run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> パッケージは並行処理下で使うことが多いだろう。
たとえば <code>run()</code> 関数をこんな感じに書き換えてみるか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">wg</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これで平行に動作している全ての <code>ticker()</code> に対してキャンセルを送り込むことができる。</p>
<p>上のコード例ではひとつの <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> インスタンスを複数の <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> で使いまわしているが,以下のように</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">wg</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>各 <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> ごとに <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> インスタンスを生成してセットしても全ての <code>ticker()</code> を <code>Ctrl+C</code> で問題なく止めることができた。</p>
<h2>キャンセル・イベントの伝搬</h2>
<p><a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> パッケージは,名前の通り,異なるレイヤやドメイン間でコンテキスト情報を受け渡しするためのパッケージだが,親から子にキャンセルイベントが伝搬する性質がある(逆向きには伝搬しない)。
たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">wg</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">child</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span><span class="nx">parent</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span><span class="o">*</span><span class="mi">5</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nf">ticker</span><span class="p">(</span><span class="nx">child</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>などとすれば各 <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> の <code>ticker()</code> 関数に <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> イベントとタイムアウト・イベントの両方を仕込むことができる。</p>
<p>また</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ctx</span><span class="p">,</span> <span class="nx">cancel</span> <span class="o">:=</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">NotifyContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span></code></pre></div><p>とした場合の返り値の <code>cancel</code> は関数値になっていて,これをキックすることでペアとなっている <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> インスタンス(上のコードなら <code>ctx</code>)にキャンセル・イベントを発生させることができる。
実際の使い方として <a href="https://golang.org/pkg/os/signal/" title="signal - The Go Programming Language"><code>signal</code></a><code>.NotifyContext()</code> 関数は main <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> に近いところで <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.WithCancel()</code> 関数と置き換えることが多いのではないだろうか。</p>
<p><a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> について詳しくは『<a href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go 言語による並行処理</a>』の 4.12 章が参考になる。
素敵なキャンセル・ライフを(笑)</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://mattn.kaoriya.net/software/lang/go/20200916090416.htm">Big Sky :: os/signal に NotifyContext が入った。</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51pUKQajnaL._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go言語による並行処理</a></dt>
<dd>Katherine Cox-Buday (著), 山口 能迪 (翻訳)</dd>
<dd>オライリージャパン 2018-10-26</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4873118468 (ASIN), 9784873118468 (EAN), 4873118468 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://www.oreilly.co.jp/books/9784873118468/">Eブック版もある</a>。感想は<a href="https://text.baldanders.info/remark/2018/11/concurrency-in-go/">こちら</a>。 Go 言語で並行処理を書くならこの本は必読書になるだろう。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2020-01-13">2020-01-13</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Go言語による並行処理 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
golangci-lint に叱られる
tag:text.Baldanders.info,2019-02-06:/golang/donot-sleep-through-life/
2019-02-06T14:15:43+00:00
2021-08-12T21:22:05+00:00
そんなことまで知っている golangci-lint は偉いねぇ。
Spiegel
https://baldanders.info/profile/
<p>私は <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>コードを <a href="https://atom.io/" title="Atom">ATOM</a> エディタおよび <a href="https://atom.io/packages/go-plus">go-plus</a> パッケージで書いているのだが,最近の <a href="https://atom.io/packages/go-plus">go-plus</a> は lint に以下のツールを選択できるようだ。</p>
<ul>
<li><a href="https://github.com/alecthomas/gometalinter" title="alecthomas/gometalinter: Concurrently run Go lint tools and normalise their output">gometalinter</a></li>
<li><a href="https://github.com/golangci/golangci-lint" title="golangci/golangci-lint: Linters Runner for Go. 5x faster than gometalinter. Nice colored output. Can report only new issues. Fewer false-positives. Yaml/toml config.">golangci-lint</a></li>
<li><a href="https://github.com/mgechev/revive" title="mgechev/revive: 🔥 ~6x faster, stricter, configurable, extensible, and beautiful drop-in replacement for golint.">revive</a></li>
</ul>
<p>特に <a href="https://github.com/golangci/golangci-lint" title="golangci/golangci-lint: Linters Runner for Go. 5x faster than gometalinter. Nice colored output. Can report only new issues. Fewer false-positives. Yaml/toml config.">golangci-lint</a> は <a href="https://github.com/alecthomas/gometalinter" title="alecthomas/gometalinter: Concurrently run Go lint tools and normalise their output">gometalinter</a> より5倍も速いと豪語してるので,こちらを試してみることにした。
<a href="https://golangci.com/" title="Automated code review for Go">GolangCI</a> も気になるが,それはまたいつか。</p>
<p>いやぁ,最近の lint は賢いんだね。
特に古いコードのまま放置している部分についてどえら叱られたですよ。
ボーっと生きててすみません(笑)</p>
<h2>error を無視すんな</h2>
<p>例えば <a href="https://golang.org/pkg/io/" title="io - The Go Programming Language"><code>io</code></a><code>.Reader</code> から <a href="https://golang.org/pkg/io/" title="io - The Go Programming Language"><code>io</code></a><code>.Writer</code> へデータを流し込むのにやっつけコードで</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="nx">writer</span><span class="p">,</span> <span class="nx">reader</span><span class="p">)</span>
</span></span></code></pre></div><p>とか書くことがあるが <a href="https://github.com/golangci/golangci-lint" title="golangci/golangci-lint: Linters Runner for Go. 5x faster than gometalinter. Nice colored output. Can report only new issues. Fewer false-positives. Yaml/toml config.">golangci-lint</a> にかけたら「返り値の error を無視すんなや」って叱られた。
ちゃんと書くなら</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="nx">writer</span><span class="p">,</span> <span class="nx">reader</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>とかすべき,ということだろう。
明示的に返り値の error を無視するなら</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="nx">writer</span><span class="p">,</span> <span class="nx">reader</span><span class="p">)</span>
</span></span></code></pre></div><p>などと明示すれば,とりあえず叱られない。
まぁ安直にこう書いてしまうのは問題だけど。</p>
<h2>true は不要</h2>
<p>無限ループについて昔は何となく</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">for</span> <span class="kc">true</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>とか書いていて,またも「true とか要らんけぇ」と叱られた。
正しくは</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>でよい。
こういう「若気の至り」なコードがそこかしこに残ってて,黒歴史を見せられてるようでちょっと恥ずかしい。</p>
<h2>SIGNAL のバッファリング</h2>
<p>SIGNAL を channel として登録する際に誤って</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Signal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">signal</span><span class="p">.</span><span class="nf">Notify</span><span class="p">(</span><span class="nx">ch</span><span class="p">,</span> <span class="nx">sig</span><span class="o">...</span><span class="p">)</span>
</span></span></code></pre></div><p>とか書いていて「ちゃんとバッファリングさせろや,ゴラァ」とまたまた叱られた。
正解は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Signal</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">signal</span><span class="p">.</span><span class="nf">Notify</span><span class="p">(</span><span class="nx">ch</span><span class="p">,</span> <span class="nx">sig</span><span class="o">...</span><span class="p">)</span>
</span></span></code></pre></div><p>そんなことまで知っている <a href="https://github.com/golangci/golangci-lint" title="golangci/golangci-lint: Linters Runner for Go. 5x faster than gometalinter. Nice colored output. Can report only new issues. Fewer false-positives. Yaml/toml config.">golangci-lint</a> は偉いねぇ。</p>
<h2>Lint は知見のかたまり</h2>
<p>というわけで過去の恥ずかしいコードが次々と発覚してしまい,公開しているコードのリファクタリングを行っている真っ最中である。
色々やりたいことがあるのに横道に逸れてばっかり。</p>
<p>C 言語で書いてた頃は MISRA-C とかいった「事実上の標準」みたいなのがあって lint ツールとかも(少なくともエンタープライズ向けは)そういったものに準拠したものが色々あった。
Lint ってのはそのプログラミング言語に関する知見のかたまりなので上手に使いこなしていきたいものである。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://mattn.kaoriya.net/software/lang/go/20190205190203.htm">Big Sky :: Lint ツールを Language Server に対応させるコマンド efm-langserver 作った。</a></li>
<li><a href="https://budougumi0617.github.io/2020/09/06/why_signal_notify_want_buffered_channel/">signal.Notifyを使うときは必ずバッファ付きチャネルで利用すること - My External Storage</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4542503461?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51CAFNAdZPL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4542503461?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">組込み開発者におくるMISRA‐C:2004―C言語利用の高信頼化ガイド</a></dt>
<dd>MISRA‐C研究会 (編集)</dd>
<dd>日本規格協会 2006-10-01</dd>
<dd>単行本</dd>
<dd>4542503461 (ASIN), 9784542503465 (EAN), 4542503461 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">私が持っているのはこれよりひとつ古い版だが,まぁいいか。むかし,車載用の組み込みエンジニアをやっていた頃は必読書として読まされました。今はもっと包括的な内容のものがあるはず。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2019-02-06">2019-02-06</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 組込み開発者におくるMISRA‐C:2004 -->
time.Ticker で遊ぶ
tag:text.Baldanders.info,2018-03-01:/golang/ticker/
2018-03-01T11:28:49+00:00
2021-08-12T21:22:05+00:00
複数の goroutine が協調して動いている場合は SIGNAL イベントに対して全ての goroutine が適切に処理を行う必要がある。
Spiegel
https://baldanders.info/profile/
<div class="box"><strong>【2021-02-18】</strong>
Go 1.16 で追加された関数を使った「<a href="https://text.baldanders.info/golang/ticker-2/">time.Ticker で遊ぶ【Go 1.16 バージョン】</a>」を公開した。
併せてどうぞ。</div>
<p>相変わらず小ネタで。
今回の目標は2つ。</p>
<ol>
<li>一定周期ごとの処理を行う</li>
<li>Ctrl+C 等の割り込み処理を行う</li>
</ol>
<h2>一定周期ごとの処理を行う</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で一定周期ごとに処理を行うには <a href="http://golang.org/pkg/time/" title="time - The Go Programming Language"><code>time</code></a><code>.Ticker</code> が使える。
以下は1秒ごとに現在時刻を表示する処理である。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">//1秒周期の ticker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">ticker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="http://golang.org/pkg/time/" title="time - The Go Programming Language"><code>time</code></a><code>.Ticker.C</code> は受信 <a href="http://golang.org/ref/spec#Channel_types" title="The Go Programming Language Specification - The Go Programming Language">channel</a> で,周期イベント発生時の時刻がセットされる。
<a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">defer</a> 構文を使って終了時に <a href="http://golang.org/pkg/time/" title="time - The Go Programming Language"><code>time</code></a><code>.Ticker.Stop()</code> 関数で周期イベントを止めようとしているが,実際には無限ループなので, return まで到達しない(笑)</p>
<p>このコードはちゃんと動くが,終了条件を記述していないので Ctrl+C などで外部から強制的に止めない限り動き続ける。</p>
<h2>SIGNAL を捕まえる</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では SIGINT や SIGTERM といった OS から送信される <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> をイベントとして <a href="http://golang.org/ref/spec#Channel_types" title="The Go Programming Language Specification - The Go Programming Language">channel</a> に送り込む仕掛けがある(ちなみに Ctrl+C は SIGINT として送られる)。
この仕掛けを組み込んだコードが以下である。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os/signal"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"syscall"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">//1秒周期の ticker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"> <span class="nx">sig</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Signal</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">signal</span><span class="p">.</span><span class="nf">Notify</span><span class="p">(</span><span class="nx">sig</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGHUP</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGQUIT</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">defer</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">Stop</span><span class="p">(</span><span class="nx">sig</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">case</span> <span class="nx">s</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">sig</span><span class="p">:</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">switch</span> <span class="nx">s</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">case</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGHUP</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGQUIT</span><span class="p">:</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop!"</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">ticker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>このやり方であれば Ctrl+C でも強制終了することなく正しく <a href="http://golang.org/pkg/time/" title="time - The Go Programming Language"><code>time</code></a><code>.Ticker.Stop()</code> 関数が起動される。</p>
<p>ここまでが main <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> のみの処理の場合。
複数の <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> が協調して動いている場合は <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> イベントに対して全ての <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> が適切に処理を行う必要がある。</p>
<h2>キャンセル・イベントの伝搬</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a> 1.7 から <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> が標準パッケージに加わった。
<a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> パッケージは,名前の通り,異なるレイヤやドメイン間でコンテキスト情報を受け渡しするためのパッケージだが,キャンセル・イベントを扱うことができる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ctx</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">parent</span><span class="p">,</span> <span class="nx">cancelParent</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithCancel</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">child</span><span class="p">,</span> <span class="nx">cancelChild</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithCancel</span><span class="p">(</span><span class="nx">parent</span><span class="p">)</span>
</span></span></code></pre></div><p><code>cancelParent</code> および <code>cancelChild</code> は関数値で,これをキックすることでそれぞれの <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> にキャンセル・イベントが発生する。
面白いのは <code>parent</code> で発生したイベントは <code>child</code> にも伝搬する点である(逆向きには伝搬しない)。
これを利用して,発生した <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> に対して親の <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> にイベントを発生させることによって全ての子 <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> にイベントを伝搬させることが可能になる。</p>
<p>たとえば,先ほどの <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> の処理は以下のように書き直すことができる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">SignalContext</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span><span class="p">,</span> <span class="nx">cancelParent</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithCancel</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nf">cancelParent</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">sig</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Signal</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">signal</span><span class="p">.</span><span class="nf">Notify</span><span class="p">(</span><span class="nx">sig</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGHUP</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGQUIT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">Stop</span><span class="p">(</span><span class="nx">sig</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">parent</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Cancel from parent"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">s</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">sig</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">s</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGHUP</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGQUIT</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">parent</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>ticker()</code> 関数のほうも <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> のイベントを拾えるよう以下のように書き直す。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">//1秒周期の ticker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop child"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>周期処理と <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> 受信処理を別々の <a href="http://golang.org/ref/spec#Go_statements" title="The Go Programming Language Specification - The Go Programming Language">goroutine</a> で駆動させて両者を <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> で繋ぐのである。</p>
<p>完全なコードは以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"context"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os/signal"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"syscall"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">//1秒周期の ticker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop child"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">SignalContext</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span><span class="p">,</span> <span class="nx">cancelParent</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithCancel</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nf">cancelParent</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">sig</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Signal</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">signal</span><span class="p">.</span><span class="nf">Notify</span><span class="p">(</span><span class="nx">sig</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGHUP</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGQUIT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">signal</span><span class="p">.</span><span class="nf">Stop</span><span class="p">(</span><span class="nx">sig</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">parent</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Cancel from parent"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">s</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">sig</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">s</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGHUP</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGINT</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGTERM</span><span class="p">,</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">SIGQUIT</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">parent</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">error</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">errCh</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span> <span class="o">:=</span> <span class="nf">SignalContext</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">child</span><span class="p">,</span> <span class="nx">cancelChild</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span><span class="nx">parent</span><span class="p">,</span> <span class="mi">10</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nf">cancelChild</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errCh</span> <span class="o"><-</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">child</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">errCh</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Done parent"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ctx</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">ctx</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Done"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ちなみに <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.WithTimeout()</code> 関数は <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> にタイムアウト・イベントを付加する。
他にもデッドライン日時を指定する <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.WithDeadline()</code> 関数がある。</p>
<p>なんか今回は久しぶりに <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>っぽいコードだったねぇ(笑)</p>
<h2>【追記】 Windows では SIGNAL を送信できない</h2>
<p>今回は <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> を受信する場合の話だったが,もちろん任意のプロセスに <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> を送信することもできる。
以下は自分自身に SIGINT を投げるコード例である<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">proc</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">FindProcess</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nf">Getppid</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="nx">proc</span><span class="p">.</span><span class="nf">Signal</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span></code></pre></div><p>ただし,このコードは Windows では(コンパイル・エラーにはならないが)動かない。
Windows 用の <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.Process.Signal()</code> 関数の実体は以下の通りだが</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Process</span><span class="p">)</span> <span class="nf">signal</span><span class="p">(</span><span class="nx">sig</span> <span class="nx">Signal</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">handle</span> <span class="o">:=</span> <span class="nx">atomic</span><span class="p">.</span><span class="nf">LoadUintptr</span><span class="p">(</span><span class="o">&</span><span class="nx">p</span><span class="p">.</span><span class="nx">handle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">handle</span> <span class="o">==</span> <span class="nb">uintptr</span><span class="p">(</span><span class="nx">syscall</span><span class="p">.</span><span class="nx">InvalidHandle</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">EINVAL</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">p</span><span class="p">.</span><span class="nf">done</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"os: process already finished"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">sig</span> <span class="o">==</span> <span class="nx">Kill</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">terminateProcess</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">Pid</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">runtime</span><span class="p">.</span><span class="nf">KeepAlive</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// TODO(rsc): Handle Interrupt too?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">syscall</span><span class="p">.</span><span class="nf">Errno</span><span class="p">(</span><span class="nx">syscall</span><span class="p">.</span><span class="nx">EWINDOWS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>このように <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.Kill</code> (SIGKILL) 以外は効かないようになっている。
理由は不明だが TODO になってるようなので何か理由があるのだろう。</p>
<p>というわけでテストができないのですよ <code>orz</code></p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://blog.golang.org/pipelines">Go Concurrency Patterns: Pipelines and cancellation - The Go Blog</a></li>
<li><a href="https://deeeet.com/writing/2016/07/22/context/">Go1.7のcontextパッケージ | SOTA</a></li>
<li><a href="http://deeeet.com/writing/2017/02/23/go-context-value/">Golangのcontext.Valueの使い方 | SOTA</a></li>
<li><a href="http://qiita.com/Kei-Kamikawa/items/620f9504daf2ec53f0b5">goroutine にシグナルを送信する - Qiita</a></li>
<li><a href="https://budougumi0617.github.io/2020/09/06/why_signal_notify_want_buffered_channel/">signal.Notifyを使うときは必ずバッファ付きチャネルで利用すること - My External Storage</a></li>
<li><a href="https://mattn.kaoriya.net/software/lang/go/20200916090416.htm">Big Sky :: os/signal に NotifyContext が入った。</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://golang.org/pkg/syscall/" title="syscall - The Go Programming Language"><code>syscall</code></a> パッケージを使うやり方もあるが,今回は割愛する。つか, Windows 版には <a href="https://golang.org/pkg/syscall/" title="syscall - The Go Programming Language"><code>syscall</code></a><code>.Kill()</code> 関数が存在しないのでやりようがないのだが。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
ルークよ, Signal を使え!
tag:text.Baldanders.info,2015-11-14:/remark/2015/use-the-signal-luke/
2015-11-13T21:25:40+00:00
2020-01-05T11:59:50+00:00
TextSecure は Signal に統合された。Signal は SMS を置き換えることができ,ローカルストレージにあるログもちゃんと暗号化される。オススメである。
Spiegel
https://baldanders.info/profile/
<p>「<a href="http://d.hatena.ne.jp/yomoyomo/20151112/jonnycantencrypt">なぜジョニーは今もやっぱり暗号化できないのか:現在のPGPクライアントの使いやすさ評価 - YAMDAS現更新履歴</a>」経由。</p>
<ul>
<li><span><a href="http://arxiv.org/pdf/1510.08555.pdf">Why Johnny Still, Still Can’t Encrypt: Evaluating the Usability of a Modern PGP Client <sup><i class="far fa-file-pdf"></i></sup></a></span></li>
<li><a href="http://boingboing.net/2015/11/06/why-do-encryption-tools-suck.html">Why do encryption tools suck? / Boing Boing</a></li>
</ul>
<p>タイトルだけ見て最初に思ったのは「PGP ってゆーな<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>」だったが,それはともかく</p>
<figure lang="en">
<blockquote>
<q>In our study of 20 participants, grouped into 10 pairs of participants who attempted to exchange encrypted email, only one pair was able to successfully complete the assigned tasks using Mailvelope. All other participants were unable to complete the assigned task in the one hour allotted to the study.
This demonstrates that encrypting email with PGP, as implemented in Mailvelope, is still unusable for the masses.</q>
</blockquote>
<figcaption><div>via <q><a href="http://arxiv.org/pdf/1510.08555.pdf">Why Johnny Still, Still Can’t Encrypt: Evaluating the Usability of a Modern PGP Client</a></q></div></figcaption>
</figure>
<p>10% 以下かよ orz</p>
<p>1990年代からカジュアルに PGP を使ってて,あまつさえ <a href="http://hp.vector.co.jp/authors/VA023900/gpg-pin/">Becky! 用のプラグイン</a>まで自作してしまった<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> 身としては,かなり衝撃的ではあったが,まぁそんなもんかなぁ。</p>
<p>ちなみに <a href="https://www.mailvelope.com/" title="Mailvelope">Mailvelope</a> は Chrome や Firefox の拡張機能として使える MUA(Mail User Agent)で, Gmail などと連携して使える。
<a href="https://baldanders.info/blog/000782/" title="安全なメッセージング・アプリとは(追記あり) — Baldanders.info">以前にも紹介</a>したが, <a href="https://www.eff.org/secure-messaging-scorecard">EFF の Secure Messaging Scorecard</a> で MUA の中では(PFS の項目を除いて<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>)唯一満点だった製品である。
<a href="https://www.mailvelope.com/" title="Mailvelope">Mailvelope</a> は <a href="https://tools.ietf.org/html/rfc4880" title="RFC 4880 - OpenPGP Message Format">OpenPGP</a> 実装を含んでいるため <a href="https://www.gnupg.org/" title="The GNU Privacy Guard">GnuPG</a> や <a href="https://www.symantec.com/encryption/" title="PGP Encryption Software | Symantec">PGP</a> のような製品を必要としない利点がある。</p>
<ul>
<li><a href="http://security.hondaclinic.jp/%E6%9A%97%E5%8F%B7%E3%81%AE%E3%81%99%E3%81%99%E3%82%81/mailvelope%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/">mailvelopeの使い方 | セキュリティの意識と知識</a></li>
</ul>
<p>この論文については Bruce Schneier さんも言及している。</p>
<figure lang="en">
<blockquote>
<q>I have recently come to the conclusion that e-mail is fundamentally unsecurable. The things we want out of e-mail, and an e-mail system, are not readily compatible with encryption. I advise people who want communications security to not use e-mail, but instead use an encrypted message client like <a href="https://otr.cypherpunks.ca/">OTR</a> or <a href="https://whispersystems.org/">Signal</a>.</q>
</blockquote>
<figcaption><div>via <q><a href="https://www.schneier.com/blog/archives/2015/11/testing_the_usa.html">Testing the Usability of PGP Encryption Tools - Schneier on Security</a></q></div></figcaption>
</figure>
<p>正論ですね。
そういうことです。</p>
<p>そういえば, <a href="https://whispersystems.org/">Open Whisper Systems</a> の TextSecure は Signal に統合された。</p>
<ul>
<li><a href="https://whispersystems.org/blog/just-signal/">Open Whisper Systems » Blog » Just Signal</a></li>
<li><a href="https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms">Signal Private Messenger - Google Play</a></li>
</ul>
<p>暗号化通話アプリの RedPhone も Signal に統合された。
Signal は SMS を置き換えることができ,ローカルストレージにあるログもちゃんと暗号化される。
オススメである。</p>
<p>いや,もう,マジ LINE とか捨てて欲しい。
Facebook もくだらない「<a href="http://www.itmedia.co.jp/news/articles/1511/13/news077.html">セキュリティ劇場</a>」で遊んでないで, OTR くらい組み込めや。</p>
<p>メッセージングの主体が PC からスマホなどの携帯端末にシフトしている状態で電子メールの有効性は下がってきていると思う。
もちろん仕事などではまだまだ PC や Workstation を使うことも多いし,対外的には電子メールが主体になると思うけど,たとえばグループウェアに XMPP+OTR を組み込んでいくなら,やはり電子メールは無用の長物になっていくだろう。</p>
<figure style='margin:0 auto;text-align:center;'>
<div style="position: relative; margin: 0 2rem; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;">
<iframe class="youtube-player" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" allowfullscreen frameborder="0" src="https://www.youtube-nocookie.com/embed/o2we_B6hDrY" allowfullscreen></iframe>
</div></figure>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51t6yHHVwEL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B015643CPE?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">暗号技術入門 第3版 秘密の国のアリス</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2015-08-25 (Release 2015-09-17)</dd>
<dd>Kindle版</dd>
<dd>B015643CPE (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2015-09-20">2015-09-20</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 暗号技術入門 第3版 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>昔はともかく,現在 <a href="https://www.symantec.com/encryption/" title="PGP Encryption Software | Symantec">PGP</a> は Symantic 社の製品名であり,いくつかある <a href="https://tools.ietf.org/html/rfc4880" title="RFC 4880 - OpenPGP Message Format">OpenPGP</a> 実装のひとつにすぎない。余談だが,確かに <a href="https://tools.ietf.org/html/rfc4880" title="RFC 4880 - OpenPGP Message Format">OpenPGP</a> 用の「公開鍵サーバ」というのは存在しているが, <a href="https://www.gnupg.org/" title="The GNU Privacy Guard">GnuPG</a> も <a href="https://www.symantec.com/encryption/" title="PGP Encryption Software | Symantec">PGP</a> も,今回紹介する <a href="https://www.mailvelope.com/" title="Mailvelope">Mailvelope</a> も別にクライアントというわけではなく,スタンドアロンで動作するプログラムである。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>現在,このプラグインはメンテナンスされてない。使わないように。 Windows パソコンでメールの暗号化がしたいなら <a href="https://www.mozilla.org/thunderbird/">Thunderbird</a>+<a href="https://addons.mozilla.org/thunderbird/addon/enigmail/">Enigmail</a> がオススメである。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p><a href="https://baldanders.info/blog/000782/" title="安全なメッセージング・アプリとは(追記あり) — Baldanders.info">前にも書いた</a>が, MUA はその性質上 PFS(Perfect Forward Secrecy)を満たせない。 PFS は通信経路の暗号化や IM(Instant Messaging)など使い捨てのメッセージを扱う場合には必要な要件だが,電子メールには向いていない。電子メールは過去のやり取りに対して「否認防止(non-repudiation)」できなければならないからだ。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>