The ONE使用笔记:读取外部事件存在BUG

The ONE仿真器读取外部事件(StandardEventsReaderreadEvents)存在BUG(感谢@馒头分享),本文描述并修复该BUG。

1. BUG描述

The ONE读取外部事件是由StandardEventsReaderreadEvents完成。默认情况下,每次读取500个,可以在设置文件修改默认值,如下:

public static final int DEFAULT_NROF_PRELOAD = 500  //ExternalEventsQueue.java

//设置文件
Events1.class = ExternalEventsQueue 
Events1.nrofPreload = …

readEvents每次读取501个事件,500个放入events,另一个没有做任何处理,所以每500个事件会有1个漏掉,以下是我某次仿真的结果,可见实际创建的消息数目少于消息创建事件的数目。

#消息事件数目  #实际创建的消息数目
20000        199601
24000        239521
26000        259482
30000        299402
36000        359282

2. BUG分析及修复

(1)BUG分析

产生该的BUG原因是readEvents先读取事件再判断循环条件,相关源代码如下:

// StandardEventsReader.java
public List<ExternalEvent> readEvents(int nrof) {
    ...
    while (eventsRead < nrof && line != null) {
        ...    
        line = this.reader.readLine(); //问题在这里!
        eventsRead++;
        ...
    }
    
    return events;
}

问题就出在第6行,先读取下一行,再判断eventsRead是否小于nrof,若不小于,则结束事件读取,这样就漏掉了一个事件。

(2)修复BUG

修改该BUG也简单,先对eventsRead做判断再决定是否读取下一个事件,如下:

line = this.reader.readLine();
eventsRead++;

// 改成:
eventsRead++;
if (eventsRead < nrof) {
    line = this.reader.readLine();
}

事实上,读取事件readEvents完全可以用do-while来做,代码会更加整洁,如下(注:以下代码没有测试):

// StandardEventsReader.java, fix the bug, events reader
public List<ExternalEvent> readEvents(int nrof) {
    ArrayList<ExternalEvent> events = new ArrayList<ExternalEvent>(nrof);
    int eventsRead = 0;
    // skip empty and comment lines
    Pattern skipPattern = Pattern.compile("(#.*)|(^\\s*$)");

    String line;
    do 
    {
        /*** Step 1: read a line ***/
        try {
            line = this.reader.readLine();
        } catch (IOException e1) {
            throw new SimError("Reading from external event file failed.");
        }
        Scanner lineScan = new Scanner(line);
        if (skipPattern.matcher(line).matches()) { // skip empty and comment lines
            continue;
        }

        /*** Step 2: process the line ***/
        double time;
        String action;
        String msgId;
        int hostAddr;
        int host2Addr;

        try {
            time = lineScan.nextDouble();
            action = lineScan.next();        
            
            /*** drop message event  ***/
            if (action.equals(DROP)) {  // public static final String DROP = "DR";
                msgId = lineScan.next();
                hostAddr = getHostAddress(lineScan.next());
                events.add(new MessageDeleteEvent(hostAddr, msgId, time, true));
            }
            /*** remove message event ***/
            else if (action.equals(REMOVE)) { // public static final String REMOVE = "R";
                msgId = lineScan.next();
                hostAddr = getHostAddress(lineScan.next());
                events.add(new MessageDeleteEvent(hostAddr, msgId, time, false));
            }
            /*** connection event ***/
            else if (action.equals(CONNECTION)) {  // public static final String CONNECTION = "CONN";
                String connEventType;
                boolean isUp;
                hostAddr = getHostAddress(lineScan.next());
                host2Addr = getHostAddress(lineScan.next());
                connEventType = lineScan.next();

                String interfaceId = null;
                if (lineScan.hasNext()) {
                    interfaceId = lineScan.next();
                }    

                if (connEventType.equalsIgnoreCase(CONNECTION_UP)) {
                    isUp = true;
                }
                else if (connEventType.equalsIgnoreCase(CONNECTION_DOWN)) {
                    isUp = false;
                }
                else {
                    throw new SimError("Unknown up/down value '" + connEventType + "'");
                }

                ConnectionEvent ce = new ConnectionEvent(hostAddr, host2Addr, interfaceId, isUp, time);

                events.add(ce);
            }
            
            else {
                msgId = lineScan.next();
                hostAddr = getHostAddress(lineScan.next());

                host2Addr = getHostAddress(lineScan.next());
                /*** create message event ***/
                if (action.equals(CREATE)){  // public static final String CREATE = "C";
                    int size = lineScan.nextInt();
                    int respSize = 0;
                    if (lineScan.hasNextInt()) {
                        respSize = lineScan.nextInt();
                    }
                    events.add(new MessageCreateEvent(hostAddr, host2Addr, msgId, size, respSize, time));
                }
                else {
                    int stage = -1;
                    /*** send message event ***/
                    if (action.equals(SEND)) { // public static final String SEND = "S";
                        stage = MessageRelayEvent.SENDING;
                    }
                    /*** delivered message event ***/
                    else if (action.equals(DELIVERED)) { // public static final String DELIVERED = "DE";
                        stage = MessageRelayEvent.TRANSFERRED;
                    }
                    /*** abort message event ***/
                    else if (action.equals(ABORT)) { // public static final String ABORT = "A";
                        stage = MessageRelayEvent.ABORTED;
                    }
                    else {
                        throw new SimError("Unknown action '" + action +  "' in external events");
                    }
                    events.add(new MessageRelayEvent(hostAddr, host2Addr, msgId, time, stage));
                }                        
            }
            // discard the newline in the end
            if (lineScan.hasNextLine()) {
                lineScan.nextLine(); // TODO: test
            }
        }catch (Exception e) {
            throw new SimError("Can't parse external event " + (eventsRead+1) + " from '" + line + "'", e);
        }
        
        eventsRead++;
    } while (eventsRead < nrof && line != null);

    return events;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

3 thoughts on “The ONE使用笔记:读取外部事件存在BUG