Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

memory from transport buffer not freed after connection_lost

オープン
#548 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
35/100
issue の種類
バグ
明瞭さ
説明が足りない
活発さ
停滞
技術スタック
python
領域
networking

調査の方向性

まず、提供された Python の再現コードを uvloop.new_event_loop で実行し、telnet セッションが終了した後のメモリを確認してから、標準の asyncio runner と比較します。transport buffer と connection_lost の経路を追跡します。asyncio の場合と同様に、切断後にメモリが解放されれば完了です。

索引モデルが issue の本文から書いたものです。

説明

  • uvloop version: 0.17.0
  • Python version: 3.11.4
  • Platform: fedora 38
  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: yes, with PYTHONASYNCIODEBUG=1
  • Does uvloop behave differently from vanilla asyncio? How?: yes, keeps resident memory

If I flood transport.write() and then close the connection (e.g. having the client telnet session stop) the memory stays resident.
Under stock asyncio, the memory immediately is released.

To reproduce,

  1. start this program,
  2. watch it in top
  3. , telnet to port 8888,
  4. watch memory usage spike.
  5. close the telnet session,
  6. see that memory usage stays high.
  7. repeat this experiment with stock asyncio runner,
  8. the memory is freed immediately. after telnet session is closed

This test script is a malloc bomb, so make sure you watch memory closely or you will consume all the memory on your machine

#!/usr/bin/env python3


import asyncio
import uvloop


class EchoServerProtocol(asyncio.Protocol):
    def __init__(self, on_connect, on_disconnect):
        self.on_connect = on_connect
        self.on_disconnect = on_disconnect

    def connection_lost(self, transport):
        print('Connection lost')
        self.on_disconnect()
        self.transport = None

    def connection_made(self, transport):
        self.on_connect(transport)
        peername = transport.get_extra_info('peername')
        print('Connection from {}'.format(peername))
        self.transport = transport

    def data_received(self, data):
        message = data.decode()
        print('Data received: {!r}'.format(message))

        print('Send: {!r}'.format(message))
        self.transport.write(data)

        print('Close the client socket')


async def send_data(transport):
    message = b'Hello World!' * 1000
    while True:
        transport.write(message)
        await asyncio.sleep(0)


task = None


def on_connect(transport):
    global task
    task = asyncio.create_task(send_data(transport))


def on_disconnect():
    global task
    task.cancel()


async def start():
    # Get a reference to the event loop as we plan to use
    # low-level APIs.
    loop = asyncio.get_running_loop()

    server = await loop.create_server(
        lambda: EchoServerProtocol(on_connect, on_disconnect),
        '127.0.0.1',
        8888,
    )

    async with server:
        await server.serve_forever()


if __name__ == '__main__':
    # with asyncio.Runner() as runner:
    with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner:
        runner.run(start())
主要言語
Cython
スター
11.9k
フォーク
615
PR マージ指標
30日以内にマージされた PR はありません

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

MagicStack/uvloop のほかの issue

MagicStack/uvloop の issue をすべて見る

似ている issue

Networking の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。