001/*
002 * This file is part of the Kompics component model runtime.
003 * <p>
004 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) Copyright (C)
005 * 2009 Royal Institute of Technology (KTH)
006 * <p>
007 * Kompics is free software; you can redistribute it and/or modify it under the
008 * terms of the GNU General Public License as published by the Free Software
009 * Foundation; either version 2 of the License, or (at your option) any later
010 * version.
011 * <p>
012 * This program is distributed in the hope that it will be useful, but WITHOUT
013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
014 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
015 * details.
016 * <p>
017 * You should have received a copy of the GNU General Public License along with
018 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
019 * Place - Suite 330, Boston, MA 02111-1307, USA.
020 */
021package se.sics.kompics;
022
023import java.util.Optional;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.UUID;
027import java.util.concurrent.ForkJoinTask;
028import java.util.concurrent.atomic.AtomicInteger;
029import java.util.concurrent.locks.ReentrantReadWriteLock;
030import org.slf4j.Logger;
031import se.sics.kompics.config.Config;
032import se.sics.kompics.config.ConfigUpdate;
033
034/**
035 * The <code>ComponentCore</code> class.
036 * <p>
037 * 
038 * @author Cosmin Arad {@literal <[email protected]>}
039 * @author Jim Dowling {@literal <[email protected]>}
040 * @author Lars Kroll {@literal <[email protected]>}
041 */
042@SuppressWarnings("serial")
043public abstract class ComponentCore extends ForkJoinTask<Void> implements Component {
044
045    private final UUID id = UUID.randomUUID();
046    protected ComponentCore parent;
047    protected Config conf;
048    public static final ThreadLocal<ComponentCore> parentThreadLocal = new ThreadLocal<ComponentCore>();
049    public static final ThreadLocal<Optional<ConfigUpdate>> childUpdate = new ThreadLocal<Optional<ConfigUpdate>>() {
050        @Override
051        protected Optional<ConfigUpdate> initialValue() {
052            return Optional.empty();
053        }
054    };
055
056    protected List<ComponentCore> children = new LinkedList<ComponentCore>();
057
058    protected final ReentrantReadWriteLock childrenLock = new ReentrantReadWriteLock();
059    protected Scheduler scheduler;
060    protected int wid;
061
062    protected abstract Logger logger();
063
064    public ComponentCore getParent() {
065        return parent;
066    }
067
068    public Config config() {
069        return conf;
070    }
071
072    protected abstract void cleanPorts();
073
074    public abstract Negative<ControlPort> createControlPort();
075
076    protected void doDestroy(Component component) {
077        ComponentCore child = (ComponentCore) component;
078        child.cleanPorts();
079        if ((child.state != State.PASSIVE) && (child.state != State.FAULTY)) {
080            logger().warn("Destroying a component before it has been stopped is not a good idea: {}",
081                    child.getComponent());
082        }
083        child.state = State.DESTROYED;
084        try {
085            childrenLock.writeLock().lock();
086
087            children.remove(child);
088        } finally {
089            childrenLock.writeLock().unlock();
090        }
091    }
092
093    protected void destroyTree(ComponentCore child) {
094        try {
095            childrenLock.writeLock().lock();
096            child.childrenLock.writeLock().lock();
097            for (ComponentCore grandchild : child.children) {
098                child.destroyTree(grandchild);
099            }
100            child.getComponent().tearDown();
101            doDestroy(child);
102        } finally {
103            child.childrenLock.writeLock().unlock();
104            childrenLock.writeLock().unlock();
105        }
106    }
107
108    protected abstract void setInactive(Component child);
109
110    protected void markSubtreeAs(State s) {
111        this.state = s;
112        if (s == State.FAULTY || s == State.DESTROYED || s == State.PASSIVE) {
113            if (parent != null) {
114                parent.setInactive(this);
115            }
116        }
117        try {
118            childrenLock.readLock().lock();
119            for (ComponentCore child : children) {
120                child.markSubtreeAs(s);
121            }
122        } finally {
123            childrenLock.readLock().unlock();
124        }
125    }
126
127    abstract void doConfigUpdate(ConfigUpdate update);
128
129    public abstract <T extends ComponentDefinition> Component doCreate(Class<T> definition,
130            Optional<Init<T>> initEvent);
131
132    public abstract <T extends ComponentDefinition> Component doCreate(Class<T> definition, Optional<Init<T>> initEvent,
133            Optional<ConfigUpdate> update);
134
135    public abstract <P extends PortType> Negative<P> createNegativePort(Class<P> portType);
136
137    public abstract <P extends PortType> Positive<P> createPositivePort(Class<P> portType);
138
139    /*
140     * === SCHEDULING ===
141     */
142    public AtomicInteger workCount = new AtomicInteger(0);
143    protected SpinlockQueue<PortCore<?>> readyPorts = new SpinlockQueue<PortCore<?>>();
144
145    /**
146     * Sets the scheduler.
147     * <p>
148     * 
149     * @param scheduler
150     *            the new scheduler
151     */
152    public void setScheduler(Scheduler scheduler) {
153        this.scheduler = scheduler;
154    }
155
156    public void eventReceived(PortCore<?> port, KompicsEvent event, int wid) {
157        // System.err.println("Received event " + event + " on " + port.getPortType().portTypeClass + " work " +
158        // workCount.get());
159        port.enqueue(event);
160        readyPorts.offer(port);
161        int wc = workCount.getAndIncrement();
162        if (wc == 0) {
163            schedule(wid);
164        }
165    }
166
167    protected void schedule(int wid) {
168        if (scheduler == null) {
169            scheduler = Kompics.getScheduler();
170        }
171        scheduler.schedule(this, wid);
172    }
173
174    public abstract void execute(int wid);
175
176    @Override
177    public void run() {
178        this.execute(0);
179    }
180
181    @Override
182    public UUID id() {
183        return this.id;
184    }
185
186    @Override
187    public boolean equals(Object obj) {
188        if (obj instanceof ComponentCore) {
189            ComponentCore cc = (ComponentCore) obj;
190            return this.id.equals(cc.id);
191        }
192        return false;
193    }
194
195    @Override
196    public int hashCode() {
197        int hash = 7;
198        hash = 59 * hash + (this.id != null ? this.id.hashCode() : 0);
199        return hash;
200    }
201
202    @Override
203    public String toString() {
204        StringBuilder sb = new StringBuilder();
205        sb.append("Component(");
206        sb.append(id);
207        sb.append("):");
208        sb.append(getComponent());
209        return sb.toString();
210    }
211
212    /*
213     * === LIFECYCLE ===
214     */
215    volatile protected Component.State state = Component.State.PASSIVE;
216
217    @Override
218    public Component.State state() {
219        return state;
220    }
221
222    /*
223     * === Relaying for package fields to Scala
224     */
225    protected void escalateFaultToKompics(Fault fault) {
226        Kompics.handleFault(fault);
227    }
228
229    protected void markSubtreeAtAs(ComponentCore source, State s) {
230        source.markSubtreeAs(s);
231    }
232
233    protected void destroyTreeAtParentOf(ComponentCore source) {
234        source.parent.destroyTree(source);
235    }
236
237    @Override
238    public Void getRawResult() {
239        return null;
240    }
241
242    @Override
243    protected void setRawResult(Void value) {
244        return;
245    }
246
247    @Override
248    protected boolean exec() {
249        try {
250            run();
251            return false;
252            // } catch(InterruptedException ex) {
253            // Thread.currentThread().interrupt();
254            // return false;
255            // }
256        } catch (Throwable e) {
257            Kompics.getFaultHandler().handle(new Fault(e, this, null));
258            throw e;
259        }
260    }
261}